A simple and customizable infinite pagination package for Flutter applications. The Easy Infinite Pagination package makes it easy to add infinite pagination to your apps. With just a few lines of code, you can create beautiful and efficient paginated lists, grids, and page views.
Easy to use: This package is designed to function similarly to standard Flutter widgets like ListView, without the need for additional controllers. Unlike many other packages, which often require custom controllers, you can think of easy_infinite_pagination
as an enhanced version of ListView
, GridView
, and PageView
.
Customizable:: You can customize the appearance of the pagination indicators as you like, Include firstPageLoading
, firstPageError
, firstPageNoItemsFound
, loadMoreLoading
, loadMoreError
, and loadMoreReachedLastPage
.
Supports list view, grid view, and page view layouts: The package can be used to create infinite lists, infinite grids, and infinite page views.
Works with any state management: The package is designed to work with any state management such as Bloc
, Riverpod
, Provider
and even the setState
.
Efficient: The package uses a number of optimizations to ensure that it is efficient, even for large datasets.
Extensible: The package can be extended to support custom pagination scenarios for example suppose that you want to add pagination for flutter_staggered_grid_view you can do that by wrapping your widget with PaginationLayoutBuilder
see Support Custom Pagination Layouts.
Import the following package in your dart file
import 'package:easy_infinite_pagination/easy_infinite_pagination.dart';
class SimpleExample extends StatefulWidget {
const SimpleExample({super.key});
@override
State<SimpleExample> createState() => _SimpleExampleState();
}
class _SimpleExampleState extends State<SimpleExample> {
List<String> _items = [];
bool _isLoading = true;
void _fetchData() async {
setState(() {
_isLoading = true;
});
await Future.delayed(const Duration(seconds: 3));
setState(() {
_isLoading = false;
// Add 20 more items to the list. This could be a network request, a database query, etc.
_items = List.generate(_items.length + 20, (i) => 'Item ${i + 1}');
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Simple Example'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: InfiniteListView(
delegate: PaginationDelegate(
itemCount: _items.length,
itemBuilder: (_, index) => ListTile(
title: Text(_items[index]),
),
isLoading: _isLoading,
onFetchData: _fetchData,
),
),
);
}
}
easy_infinite_pagination
Layout | Easy Infinite Pagination |
---|---|
ListView.builder | InfiniteListView |
ListView.separated | InfiniteListView.separated |
SliverList.builder | SliverInfiniteListView |
SliverList.separated | SliverInfiniteListView.separated |
SliverFixedExtentList | SliverInfiniteListView (just provide itemExtent parameter) |
SliverPrototypeExtentList | SliverInfiniteListView (just provide prototypeItem parameter) |
GridView.builder | InfiniteGridView |
SliverGrid.builder | SliverInfiniteGridView |
PageView.builder | InfinitePageView |
PaginationLayoutBuilder
: for custom pagination layouts scenarioseasy_infinite_pagination
you can customize the appearance of the pagination indicators as you like. Include firstPageLoading
, firstPageError
, firstPageNoItemsFound
, loadMoreLoading
, loadMoreError
, and loadMoreReachedLastPage
.InfiniteListView(
delegate: PaginationDelegate(
itemCount: _items.length,
itemBuilder: (context, index) => ListTile(
title: DefaultListTile(index: index),
),
isLoading: _isLoading,
onFetchData: _fetchData,
firstPageLoadingBuilder: (context) =>
const FirstPageLoadingIndicator(),
firstPageErrorBuilder: (context, onRetry) => FirstPageErrorIndicator(
onRetry: onRetry,
),
firstPageNoItemsBuilder: (context) =>
const FirstPageNoItemsFoundedIndicator(),
loadMoreLoadingBuilder: (context) => const LoadMoreLoadingIndicator(),
loadMoreErrorBuilder: (context, onRetry) => LoadMoreErrorIndicator(
onRetry: onRetry,
),
loadMoreNoMoreItemsBuilder: (context) =>
const LoadMoreNoMoreItemsIndicator(),
),
),
easy_infinite_pagination
you can control when to fetch data. by using the invisibleItemsThreshold
parameter and fetchDataOnStart
parameter.invisibleItemsThreshold
is used as a threshold to determine when to fetch more data default value is 3.fetchDataOnStart
is used to determine whether to fetch data on start or not default value is true.InfiniteListView(
delegate: PaginationDelegate(
// The number of remaining invisible items that should trigger a new fetch.
// The default value is 3.
invisibleItemsThreshold: 5,
// If true, it will fetch data on start.
fetchDataOnStart: true,
....
),
)
easy_infinite_pagination
with Bloceasy_infinite_pagination
package is designed to work with any state management such as Bloc
, Riverpod
, Provider
and even the setState
.InfiniteListView.separated(
delegate: PaginationDelegate(
isLoading: state is PostsFetchLoading,
hasError: state is PostsFetchError,
hasReachedMax: context.read<PostsListCubit>().hasReachedMax,
// The number of remaining invisible items that should trigger a new fetch.
// The default value is 3.
invisibleItemsThreshold: 5,
itemCount: posts.length,
itemBuilder: (context, index) {
final post = posts[index];
return PostWidget(post: post);
},
// here we add a custom error screen if the state is an error state.
// and this screen will be shown if an error occurs while fetching data for the first page.
firstPageErrorBuilder: state is PostsFetchError
? (context, onRetry) =>
CustomErrorScreen(errorMessage: state.message, onRetry: onRetry)
: null,
// this method will be called when the user reaches the end of the list or for the first page.
onFetchData: () async {
await context.read<PostsListCubit>().fetchPosts();
},
),
separatorBuilder: (context, index) => const Divider(),
)
easy_infinite_pagination
with Sliver
sliver
layouts such as SliverInfiniteListView
, SliverInfiniteListView.separated
and SliverInfiniteGridView
.SliverPadding(
padding: const EdgeInsets.all(16.0),
sliver: SliverInfiniteGridView(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
),
delegate: PaginationDelegate(
isLoading: state.isLoading,
hasError: state.hasError,
hasReachedMax: state.hasReachedMax,
// The number of remaining invisible items that should trigger a new fetch.
// The default value is 3.
invisibleItemsThreshold: 5,
itemCount: state.posts.length,
itemBuilder: (context, index) {
final post = state.posts[index];
return PostGridWidget(post: post);
},
// this method will be called when the user reaches the end of the list or for the first page.
onFetchData: () async {
await context.read<PostsGridCubit>().fetchPosts();
},
),
),
)
InfinitePageView
InfinitePageView
.InfinitePageView(
scrollDirection: Axis.vertical,
delegate: PaginationDelegate(
itemCount: _items.length,
itemBuilder: (_, index) => ListTile(
title: PageViewItem(index: index),
),
isLoading: _isLoading,
invisibleItemsThreshold: 1,
onFetchData: _fetchData,
),
),
easy_infinite_pagination
easy_infinite_pagination
package to support searching, sorting and filtering. The optimal approach depends on your state management.InfiniteListView
, SliverInfiniteListView
, InfiniteGridView
,SliverInfiniteGridView
, andInfinitePageView
do not meet your requirements, with easy_infinite_pagination
you can make your own pagination layout.PaginationLayoutBuilder
.return PaginationLayoutBuilder(
// Provider the layout strategy (box or sliver).
// In this case we used the box strategy.
// because the child not sliver widget.
layoutStrategy: LayoutStrategy.box,
delegate: paginationDelegate,
useShrinkWrapForFirstPageIndicators: _useShrinkWrapForFirstPageIndicators,
layoutChildBuilder: (
context,
itemBuilder,
itemCount,
bottomLoaderBuilder,
) =>
MasonryGridView.custom(
gridDelegate: gridDelegate,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
padding: padding,
// Here we create a predefined sliver delegate by the package
// this delegate is used to handle the bottom loader widget while loading, success and error state.
childrenDelegate: PaginationLayoutBuilder.createSliverChildDelegate(
builder: itemBuilder,
childCount: itemCount,
bottomLoaderBuilder: bottomLoaderBuilder,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
),
),
)
We hope you find the Easy Infinite Pagination package helpful!