Pagination and Infinite Lists
When you need to display large lists of data from an API, loading everything at once is rarely practical. APIs often provide paginationβa way to fetch data in chunks or "pages"βso your app only loads and displays a limited number of items at a time. This approach is essential for performance, usability, and network efficiency, especially when dealing with thousands of records.
main.dart
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100import 'package:flutter/material.dart'; import 'dart:async'; import 'dart:math'; void main() { runApp(PaginatedListApp()); } class PaginatedListApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Pagination Demo', home: PaginatedListScreen(), ); } } class PaginatedListScreen extends StatefulWidget { @override _PaginatedListScreenState createState() => _PaginatedListScreenState(); } class _PaginatedListScreenState extends State<PaginatedListScreen> { final ScrollController _scrollController = ScrollController(); final List<String> _items = []; bool _isLoading = false; int _currentPage = 1; final int _pageSize = 20; bool _hasMore = true; @override void initState() { super.initState(); _fetchPage(); _scrollController.addListener(_onScroll); } @override void dispose() { _scrollController.dispose(); super.dispose(); } Future<List<String>> _fetchData(int page, int pageSize) async { // Simulate API delay await Future.delayed(Duration(seconds: 2)); // Simulate total 100 items in API int totalItems = 100; int start = (page - 1) * pageSize; if (start >= totalItems) return []; int end = min(start + pageSize, totalItems); return List.generate(end - start, (i) => 'Item οΏ½24{start + i + 1}'); } void _fetchPage() async { if (_isLoading || !_hasMore) return; setState(() { _isLoading = true; }); List<String> newItems = await _fetchData(_currentPage, _pageSize); setState(() { _items.addAll(newItems); _isLoading = false; _hasMore = newItems.length == _pageSize; if (_hasMore) _currentPage++; }); } void _onScroll() { if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 200 && !_isLoading && _hasMore) { _fetchPage(); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Paginated List')), body: ListView.builder( controller: _scrollController, itemCount: _items.length + (_isLoading || _hasMore ? 1 : 0), itemBuilder: (context, index) { if (index < _items.length) { return ListTile(title: Text(_items[index])); } else if (_hasMore || _isLoading) { return Padding( padding: const EdgeInsets.all(16.0), child: Center(child: CircularProgressIndicator()), ); } return SizedBox.shrink(); }, ), ); } }
To avoid duplicate API calls during infinite scrolling, always check if a request is already in progress before starting a new one. Use a flag like '_isLoading' to prevent multiple simultaneous requests, which can lead to repeated data or wasted bandwidth.
The code above demonstrates a paginated ListView that fetches more items from a simulated API as you scroll. A ScrollController listens for the user's scroll position. When the user nears the end of the list, the controller triggers another API call to fetch the next page. The _currentPage variable keeps track of which page to request next, and _hasMore ensures that fetching stops when all data has been loaded. This pattern efficiently loads new items only as needed, providing a smooth infinite scrolling experience.
Thanks for your feedback!
Ask AI
Ask AI
Ask anything or try one of the suggested questions to begin our chat
Awesome!
Completion rate improved to 6.67
Pagination and Infinite Lists
Swipe to show menu
When you need to display large lists of data from an API, loading everything at once is rarely practical. APIs often provide paginationβa way to fetch data in chunks or "pages"βso your app only loads and displays a limited number of items at a time. This approach is essential for performance, usability, and network efficiency, especially when dealing with thousands of records.
main.dart
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100import 'package:flutter/material.dart'; import 'dart:async'; import 'dart:math'; void main() { runApp(PaginatedListApp()); } class PaginatedListApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Pagination Demo', home: PaginatedListScreen(), ); } } class PaginatedListScreen extends StatefulWidget { @override _PaginatedListScreenState createState() => _PaginatedListScreenState(); } class _PaginatedListScreenState extends State<PaginatedListScreen> { final ScrollController _scrollController = ScrollController(); final List<String> _items = []; bool _isLoading = false; int _currentPage = 1; final int _pageSize = 20; bool _hasMore = true; @override void initState() { super.initState(); _fetchPage(); _scrollController.addListener(_onScroll); } @override void dispose() { _scrollController.dispose(); super.dispose(); } Future<List<String>> _fetchData(int page, int pageSize) async { // Simulate API delay await Future.delayed(Duration(seconds: 2)); // Simulate total 100 items in API int totalItems = 100; int start = (page - 1) * pageSize; if (start >= totalItems) return []; int end = min(start + pageSize, totalItems); return List.generate(end - start, (i) => 'Item οΏ½24{start + i + 1}'); } void _fetchPage() async { if (_isLoading || !_hasMore) return; setState(() { _isLoading = true; }); List<String> newItems = await _fetchData(_currentPage, _pageSize); setState(() { _items.addAll(newItems); _isLoading = false; _hasMore = newItems.length == _pageSize; if (_hasMore) _currentPage++; }); } void _onScroll() { if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 200 && !_isLoading && _hasMore) { _fetchPage(); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Paginated List')), body: ListView.builder( controller: _scrollController, itemCount: _items.length + (_isLoading || _hasMore ? 1 : 0), itemBuilder: (context, index) { if (index < _items.length) { return ListTile(title: Text(_items[index])); } else if (_hasMore || _isLoading) { return Padding( padding: const EdgeInsets.all(16.0), child: Center(child: CircularProgressIndicator()), ); } return SizedBox.shrink(); }, ), ); } }
To avoid duplicate API calls during infinite scrolling, always check if a request is already in progress before starting a new one. Use a flag like '_isLoading' to prevent multiple simultaneous requests, which can lead to repeated data or wasted bandwidth.
The code above demonstrates a paginated ListView that fetches more items from a simulated API as you scroll. A ScrollController listens for the user's scroll position. When the user nears the end of the list, the controller triggers another API call to fetch the next page. The _currentPage variable keeps track of which page to request next, and _hasMore ensures that fetching stops when all data has been loaded. This pattern efficiently loads new items only as needed, providing a smooth infinite scrolling experience.
Thanks for your feedback!