Advanced Pagination - Flutter
A controller-first pagination package for Flutter with page/offset/cursor support, clean widgets, cache + prefetch, and customizable loading/empty/error UI.
Features
- Page, offset, and cursor-based pagination.
PagedListView,PagedGridView, and sliver variants.- Scroll prefetch to load ahead of the viewport.
- Cache manager with stale-while-revalidate support.
PagedLayoutBuilderand per-widget builders for custom states.
Installation
Add to your pubspec.yaml:
dependencies:
advanced_pagination: ^0.1.0
Then run:
flutter pub get
Quick Start
1) Create a controller
final controller = PaginationController.pageBased(
firstPageKey: 1,
pageSize: 20,
fetchPage: (request) async {
final page = request.pageKey ?? 1;
final items = await api.fetchPage(page, request.pageSize);
return PageResult(
items: items,
hasNext: page < 10,
hasPrevious: page > 1,
);
},
);
2) Render a list
PagedListView<int, Article>(
controller: controller,
itemBuilder: (context, item, index) => ArticleTile(item),
);
Controller Types
Page-based
final controller = PaginationController.pageBased(
firstPageKey: 1,
pageSize: 20,
fetchPage: (request) async {
final page = request.pageKey ?? 1;
final items = await api.fetchPage(page, request.pageSize);
return PageResult(
items: items,
hasNext: page < 10,
hasPrevious: page > 1,
);
},
);
Offset-based
final controller = PaginationController.offsetBased(
initialOffset: 0,
pageSize: 20,
fetchPage: (request) async {
final offset = request.pageKey ?? 0;
final items = await api.fetchOffset(offset, request.pageSize);
return PageResult(
items: items,
hasNext: items.isNotEmpty,
hasPrevious: offset > 0,
);
},
);
Cursor-based
final controller = PaginationController.cursorBased<String, Article>(
initialCursor: "0",
pageSize: 20,
fetchPage: (request) async {
final result = await api.fetchCursor(request.pageKey, request.pageSize);
return PageResult(
items: result.items,
hasNext: result.nextCursor != null,
hasPrevious: result.prevCursor != null,
nextKey: result.nextCursor,
prevKey: result.prevCursor,
);
},
);
Widgets
PagedGridView
PagedGridView<int, Article>(
controller: controller,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
),
itemBuilder: (context, item, index) => ArticleTile(item),
);
Sliver list/grid
CustomScrollView(
slivers: [
const SliverAppBar(title: Text("Sliver Grid")),
PagedSliverGrid<int, Article>(
controller: controller,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
childAspectRatio: 1.6,
),
itemBuilder: (context, item, index) => ArticleTile(item),
),
],
);
Custom States
PagedLayoutBuilder
PagedLayoutBuilder<int, Article>(
controller: controller,
loadingBuilder: (_) => const Center(child: CircularProgressIndicator()),
emptyBuilder: (_) => const Center(child: Text("No items")),
errorBuilder: (_, error) => const Center(child: Text("Something went wrong")),
builder: (context, state) {
return PagedListView<int, Article>(
controller: controller,
itemBuilder: (context, item, index) => ArticleTile(item),
);
},
);
Per-widget builders
PagedListView<int, Article>(
controller: controller,
loadingBuilder: (_) => const CircularProgressIndicator(),
statusBuilder: (context, status) {
if (status == PaginationStatus.error) {
return const Center(child: Text("Oops"));
}
return const SizedBox.shrink();
},
itemBuilder: (context, item, index) => ArticleTile(item),
);
Scroll Prefetch
PagedListView<int, Article>(
controller: controller,
enableScrollPrefetch: true,
prefetchOffset: 700,
preloadOffset: 200,
itemBuilder: (context, item, index) => ArticleTile(item),
);
Cache + Stale-While-Revalidate
final cache = MemoryPaginationCacheManager<int, Article>(maxEntries: 10);
final controller = PaginationController.pageBased(
firstPageKey: 1,
pageSize: 20,
fetchPage: (request) async { /* ... */ },
cacheManager: cache,
cacheKey: "articles-feed",
cachePolicy: const PaginationCachePolicy(
maxAge: Duration(minutes: 10),
staleWhileRevalidate: true,
),
);
Parameters (Most Used)
autoLoad: iftrue, loads the first page after build.enableRefresh: enables pull-to-refresh inPagedListView.preloadOffset: how close to the end beforefetchNext()triggers.enableScrollPrefetch: enables earlyprefetchNext().prefetchOffset: how early prefetch starts.loadingBuilder: custom loader widget (defaults toCircularProgressIndicator).
Controller API
refresh(): reload from start.fetchNext(): append next page.fetchPrevious(): prepend previous page.prefetchNext(): silent prefetch (no loader).retry(): repeat last failed request.cancel(): cancel active request.
Quick FAQ
You can read the Quick-FAQ here: https://github.com/patoliavishal/flutter_advanced_pagination/wiki/Quick-FAQ
Running the Example App
The example is a full Flutter app with android/ and ios/ folders.
cd example
flutter pub get
flutter run
For iOS on macOS, run flutter run and allow Xcode to handle signing if prompted.
Additional Information
Pull requests and feedback are welcome. If you encounter any issues, please open a GitHub issue and include a minimal reproducible example.