데이터 페이징(Data Paging)은 대량의 데이터를 화면에 한꺼번에 불러오는 대신, 페이지 단위로 나누어 점진적으로 데이터를 불러오는 방법입니다. 페이징을 통해 성능을 최적화하고, 사용자에게 더 나은 경험을 제공할 수 있습니다. Flutter에서는 리스트뷰(ListView), 페이징 컨트롤러, 외부 패키지 등을 이용하여 데이터 페이징을 구현할 수 있습니다. 이번 글에서는 Flutter에서 데이터 페이징을 구현하는 방법과 다양한 기법을 설명하겠습니다.
1. 데이터 페이징이란?
데이터 페이징은 주로 대규모 데이터 집합을 효율적으로 관리하기 위한 방법으로, 데이터를 일정한 크기의 페이지로 나누어 필요한 만큼씩 불러옵니다. 이 방식은 서버와 클라이언트 간의 데이터 전송량을 줄이고, UI를 더욱 부드럽고 응답성 있게 만드는 데 도움을 줍니다.
페이징은 다음과 같은 상황에서 사용됩니다:
- 대규모 데이터 처리: 예를 들어, 뉴스 피드, 제품 목록, 사용자 목록 등 수천 개 이상의 항목을 다룰 때.
- 성능 최적화: 데이터 로딩 시 처음부터 모든 데이터를 불러오지 않고, 사용자 스크롤 시점에 따라 점진적으로 데이터를 로드.
- 무한 스크롤 구현: 사용자가 리스트의 끝에 도달할 때마다 다음 데이터를 자동으로 로드하는 방식.
2. 기본 ListView에서 페이징 구현
Flutter의 ListView.builder 위젯을 사용하면 동적으로 데이터를 추가하면서 리스트를 표시할 수 있습니다. 이 방식으로 사용자가 스크롤할 때마다 새로운 페이지의 데이터를 불러올 수 있습니다.
2.1 ListView로 간단한 페이징 구현
다음은 ListView.builder와 _loadMoreItems 함수를 사용하여 페이징을 구현하는 기본적인 예제입니다.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: PagingExample(),
);
}
}
class PagingExample extends StatefulWidget {
@override
_PagingExampleState createState() => _PagingExampleState();
}
class _PagingExampleState extends State<PagingExample> {
List<int> _items = List.generate(20, (index) => index); // 초기 데이터
bool _isLoading = false;
Future<void> _loadMoreItems() async {
if (_isLoading) return;
setState(() {
_isLoading = true;
});
await Future.delayed(Duration(seconds: 2)); // 데이터를 불러오는 시뮬레이션
setState(() {
_items.addAll(List.generate(20, (index) => _items.length + index));
_isLoading = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Data Paging Example')),
body: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
if (!_isLoading && scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) {
_loadMoreItems();
}
return true;
},
child: ListView.builder(
itemCount: _items.length + 1,
itemBuilder: (context, index) {
if (index == _items.length) {
return _isLoading ? Center(child: CircularProgressIndicator()) : SizedBox.shrink();
}
return ListTile(
title: Text('Item ${_items[index]}'),
);
},
),
),
);
}
}
- ListView.builder: 스크롤 가능한 리스트를 동적으로 생성하며, 항목이 필요한 시점에만 데이터를 불러옵니다.
- NotificationListener: 스크롤 이벤트를 감지하여 사용자가 리스트의 끝에 도달하면 추가 데이터를 로드하는 기능을 제공합니다.
- 페이징 로직: 스크롤이 끝에 도달할 때마다 새로운 데이터를 비동기적으로 불러오며, 로딩 중인 상태를 관리합니다.
3. 무한 스크롤(Infinite Scroll) 구현
무한 스크롤(Infinite Scroll)은 사용자가 리스트의 끝에 도달할 때 새로운 데이터를 자동으로 로드하는 방식입니다. 이는 페이징과 유사하지만, 사용자가 명시적으로 페이지를 이동하지 않고, 계속해서 스크롤을 통해 다음 데이터를 확인할 수 있습니다.
3.1 무한 스크롤 구현 예제
아래는 무한 스크롤을 구현하는 간단한 예제입니다.
class InfiniteScrollExample extends StatefulWidget {
@override
_InfiniteScrollExampleState createState() => _InfiniteScrollExampleState();
}
class _InfiniteScrollExampleState extends State<InfiniteScrollExample> {
List<int> _items = List.generate(30, (index) => index);
bool _isLoading = false;
ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent && !_isLoading) {
_loadMoreItems();
}
});
}
Future<void> _loadMoreItems() async {
setState(() {
_isLoading = true;
});
await Future.delayed(Duration(seconds: 2)); // 데이터 로드 시뮬레이션
setState(() {
_items.addAll(List.generate(30, (index) => _items.length + index));
_isLoading = false;
});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Infinite Scroll Example')),
body: ListView.builder(
controller: _scrollController,
itemCount: _items.length + 1,
itemBuilder: (context, index) {
if (index == _items.length) {
return _isLoading ? Center(child: CircularProgressIndicator()) : SizedBox.shrink();
}
return ListTile(
title: Text('Item ${_items[index]}'),
);
},
),
);
}
}
- ScrollController: 스크롤 위치를 추적하여 사용자가 리스트의 끝에 도달하면 새로운 데이터를 로드하는 트리거 역할을 합니다.
- 자동 데이터 로드: 사용자가 스크롤할 때마다 데이터를 계속 불러와 화면에 추가합니다.
4. PaginatedDataTable을 사용한 테이블 페이징
Flutter에서 데이터를 테이블 형태로 표시할 때 PaginatedDataTable 위젯을 사용할 수 있습니다. 이 위젯은 테이블 데이터를 페이지 단위로 나누어 한 번에 일정량만 보여줍니다.
4.1 PaginatedDataTable 사용 예제
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: PaginatedTableExample(),
);
}
}
class PaginatedTableExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Paginated Data Table')),
body: PaginatedDataTable(
header: Text('Users'),
columns: [
DataColumn(label: Text('ID')),
DataColumn(label: Text('Name')),
],
source: UserDataSource(),
rowsPerPage: 5,
),
);
}
}
class UserDataSource extends DataTableSource {
final List<Map<String, String>> _data = List.generate(
50,
(index) => {"id": index.toString(), "name": "User $index"},
);
@override
DataRow getRow(int index) {
return DataRow(cells: [
DataCell(Text(_data[index]['id']!)),
DataCell(Text(_data[index]['name']!)),
]);
}
@override
bool get isRowCountApproximate => false;
@override
int get rowCount => _data.length;
@override
int get selectedRowCount => 0;
}
- PaginatedDataTable: 페이징 기능을 내장한 테이블 위젯으로, DataTableSource와 함께 데이터를 페이지 단위로 나누어 표시합니다.
- DataTableSource: 테이블 데이터를 제공하는 클래스이며, 페이징 및 데이터 로드 방식을 관리합니다.
5. 외부 패키지를 사용한 페이징
Flutter에서 데이터를 페이징할 때, 더 효율적이고 관리하기 쉬운 방식으로 외부 패키지를 사용할 수 있습니다. 그중 널리 사용되는 패키지로는 infinite_scroll_pagination이 있습니다.
5.1 infinite_scroll_pagination 패키지 사용 예제
먼저 pubspec.yaml에 패키지를 추가합니다
.
dependencies:
infinite_scroll_pagination: ^3.0.0
그다음, 패키지를 사용하여 페이징을 구현합니다.
import 'package:flutter/material.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: PaginationWithExternalPackage(),
);
}
}
class PaginationWithExternalPackage extends StatefulWidget {
@override
_PaginationWithExternalPackageState createState() => _PaginationWithExternalPackageState();
}
class _PaginationWithExternalPackageState extends State<PaginationWithExternalPackage> {
static const _pageSize = 20;
final PagingController<int, int> _pagingController = PagingController(firstPageKey: 0);
@override
void initState() {
_pagingController.addPageRequestListener((pageKey) {
_fetchPage(pageKey);
});
super.initState();
}
Future<void> _fetchPage(int pageKey) async {
try {
await Future.delayed(Duration(seconds: 2)); // 데이터 로드 시뮬레이션
final newItems = List.generate(_pageSize, (index) => pageKey + index);
final isLastPage = newItems.length < _pageSize;
if (isLastPage) {
_pagingController.appendLastPage(newItems);
} else {
final nextPageKey = pageKey + newItems.length;
_pagingController.appendPage(newItems, nextPageKey);
}
} catch (error) {
_pagingController.error = error;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Pagination Example')),
body: PagedListView<int, int>(
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<int>(
itemBuilder: (context, item, index) => ListTile(
title: Text('Item $item'),
),
),
),
);
}
@override
void dispose() {
_pagingController.dispose();
super.dispose();
}
}
- PagingController: 페이징 데이터와 페이지 요청을 관리하는 컨트롤러입니다.
- PagedListView: 페이징된 데이터를 보여주는 리스트뷰로, PagingController와 함께 동작합니다.
- appendPage와 appendLastPage: 새로운 페이지 데이터를 추가하거나 마지막 페이지를 표시할 때 사용됩니다.
결론
Flutter에서 데이터 페이징은 대량의 데이터를 효율적으로 관리하고 사용자 경험을 향상시키는 중요한 기술입니다. ListView.builder를 사용한 기본 페이징부터, ScrollController를 사용한 무한 스크롤, PaginatedDataTable을 이용한 테이블 페이징, 그리고 외부 패키지를 사용한 페이징까지 다양한 방법으로 데이터를 페이징할 수 있습니다. 상황에 맞는 페이징 기법을 선택하여 애플리케이션의 성능을 최적화하고, 사용자에게 부드러운 데이터 탐색 경험을 제공하세요.
'Flutter' 카테고리의 다른 글
Flutter에서 Pie Chart 사용법 (0) | 2025.03.17 |
---|---|
Flutter의 그래프와 차트(Graphs and Charts) 사용법 (0) | 2025.03.17 |
Flutter의 데이터 정렬(Data Sorting) 사용법 (0) | 2025.03.16 |
Flutter의 데이터 필터링(Data Filtering) 사용법 (0) | 2025.03.16 |
Flutter의 데이터 바인딩(Data Binding) 사용법 (0) | 2025.03.16 |