flutter_pagination_pro 0.4.0 copy "flutter_pagination_pro: ^0.4.0" to clipboard
flutter_pagination_pro: ^0.4.0 copied to clipboard

Flutter pagination: generic page keys (int, cursor, offset), infinite scroll, load more button, grid view, slivers, numbered pagination, controlled mode, updateFetchPage for search/filter, built-in lo [...]

Flutter Pagination Pro

Pub Version License: MIT Flutter

A lightweight Flutter pagination package.
Infinite scroll, load more, grid, slivers, numbered pages — all in one.

Flutter Pagination Pro Preview

Why This Package? #

Feature flutter_pagination_pro infinite_scroll_pagination
Zero dependencies ❌ (sliver_tools)
Generic page keys (int/cursor/offset)
ListView + GridView
Sliver variants
Numbered pagination
Load more button mode
Controlled mode (BYO state) .controlled()
updateFetchPage (search/filter)
Pull-to-refresh ✅ built-in Manual
initialItems (cache-first)
pageSize auto last-page
totalItems tracking
findChildIndexCallback
Header / Footer params
Skeleton loading builder
Testing utilities & matchers
Separator support
Item mutation helpers ✅ (updateItems, removeWhere, insertItem)
Type-safe generics
Accessibility (semantics) Partial

Quick Start #

dependencies:
  flutter_pagination_pro: ^0.4.0

4 Lines to Paginated List #

PagedListView<User>(
  fetchPage: (page) => api.getUsers(page: page),
  itemBuilder: (context, user, index) => ListTile(title: Text(user.name)),
)

That's it. No initialPageKey, no extra type parameter. Handles loading, errors, empty state, and infinite scroll automatically.

PagedListView<T> is a shorthand for PaginationListView<int, T> with initialPageKey defaulting to 1. Same for PagedGridView<T>, PagedController<T>, SliverPagedList<T>, SliverPagedGrid<T>.

Cursor-Based (e.g. Firestore, GraphQL) #

PaginationListView<String, User>(
  fetchPage: (cursor) => api.getUsers(cursor: cursor),
  initialPageKey: '',
  nextPageKeyBuilder: (_, items) => items.last.cursor,
  itemBuilder: (context, user, index) => ListTile(title: Text(user.name)),
)

All Modes #

// Grid
PagedGridView<Photo>(
  fetchPage: (page) => api.getPhotos(page: page),
  itemBuilder: (context, photo, index) => PhotoCard(photo: photo),
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
)

// Load More Button
PagedListView<User>(
  fetchPage: (page) => api.getUsers(page: page),
  itemBuilder: (context, user, index) => UserCard(user: user),
  paginationType: PaginationType.loadMore,
)

// Controlled Mode (BYO state)
PaginationListView<int, User>.controlled(
  items: users,
  status: PaginationStatus.loaded,
  onLoadMore: () => bloc.add(LoadNextPage()),
  itemBuilder: (context, user, index) => UserCard(user: user),
)

// Numbered Pagination
NumberedPagination(
  totalPages: 20,
  currentPage: _page,
  onPageChanged: (page) => setState(() => _page = page),
)

Slivers (CustomScrollView) #

CustomScrollView(
  controller: scrollController,
  slivers: [
    SliverAppBar(title: Text('Users'), floating: true),
    SliverPaginatedList<int, User>(
      controller: controller,
      scrollController: scrollController,
      itemBuilder: (context, user, index) => ListTile(title: Text(user.name)),
    ),
  ],
)

SliverPaginatedGrid works the same way — just add a gridDelegate.

Controller #

final controller = PagedController<User>(
  fetchPage: (page) => api.getUsers(page: page),
  config: PaginationConfig(pageSize: 20),  // auto-detects last page
  initialItems: cachedUsers,                // show cached data instantly
);

// Use with widget
PagedListView<User>.withController(
  controller: controller,
  itemBuilder: (context, user, index) => UserTile(user: user),
)

// Search / filter: swap the data source at runtime
controller.updateFetchPage(
  (page) => api.searchUsers(page: page, query: 'john'),
);
Method Description
refresh() Reload from first page (items stay visible)
retry() Retry last failed request
reset() Clear everything to initial state
loadNextPage() Manually trigger next page
updateFetchPage(fn) Replace data source + reload (search/filter)
setTotalItems(n) Set total for "Showing X of Y" + auto-complete
updateItems(fn) Transform items in-place
removeWhere(fn) Remove matching items
insertItem(i, item) Insert at index
removeItemAt(i) Remove at index
updateItemAt(i, item) Replace item at index
Property Type Description
items List<T> All loaded items
currentPageKey K? Last loaded page key
initialPageKey K First page key
status PaginationStatus Current state
hasMorePages bool More pages available?
state.totalItems int? Total from API (if set)

Customization #

Override any state widget:

PagedListView<User>(
  fetchPage: (page) => api.getUsers(page: page),
  itemBuilder: (context, user, index) => UserTile(user: user),
  firstPageLoadingBuilder: (context) => MyShimmer(),
  firstPageErrorBuilder: (context, error, retry) => MyErrorWidget(error, retry),
  emptyBuilder: (context) => MyEmptyState(),
  loadMoreLoadingBuilder: (context) => MyLoadingSpinner(),
  loadMoreErrorBuilder: (context, error, retry) => MyRetryBar(error, retry),
  endOfListBuilder: (context) => Text('All caught up!'),
  enablePullToRefresh: true,
  separatorBuilder: (context, index) => Divider(),
)

Add a header or footer that scrolls with the items — no need to switch to CustomScrollView yourself:

PagedListView<User>(
  fetchPage: (page) => api.getUsers(page: page),
  itemBuilder: (context, user, index) => UserTile(user: user),
  header: Padding(
    padding: EdgeInsets.all(16),
    child: Text('All Users', style: TextStyle(fontSize: 24)),
  ),
  footer: Center(child: Text('End of list')),
)

Available on both PaginationListView and PaginationGridView (all constructors).

Skeleton / Shimmer Loading #

Use DefaultFirstPageLoading.builder() for skeleton placeholder loading:

PagedListView<User>(
  fetchPage: (page) => api.getUsers(page: page),
  itemBuilder: (context, user, index) => UserTile(user: user),
  firstPageLoadingBuilder: (context) => DefaultFirstPageLoading.builder(
    itemBuilder: (context, index) => ShimmerUserTile(),
    itemCount: 10,
    separatorBuilder: (context, index) => Divider(height: 1),
  ),
)

Pair with the shimmer package for animated shimmer effects — this package stays lightweight.

Testing Utilities #

Import testing.dart for pre-built test helpers and matchers:

import 'package:flutter_pagination_pro/testing.dart';

final controller = testPaginationController<int, User>(
  items: [user1, user2, user3],
  status: PaginationStatus.loaded,
  currentPageKey: 1,
);

expect(controller, hasItemCount(3));
expect(controller, isOnPage(1));
expect(controller, hasStatus(PaginationStatus.loaded));
expect(controller, isPaginationCompleted);  // status + hasMorePages
expect(controller, hasPaginationError());   // any error status
expect(controller, isPaginationEmpty);      // empty status + no items

Configuration #

PaginationConfig #

Param Default Description
scrollThreshold 200.0 Pixels from bottom to trigger load
autoLoadFirstPage true Auto-load on build
pageSize null Items per page — auto-detects last page

NumberedPaginationConfig #

Param Default Description
buttonSize 40 Page button size
spacing 4 Button spacing
borderRadius 8 Button border radius
showFirstLastButtons true Show ⏮ ⏭ buttons
showNavigationButtons true Show ◀ ▶ buttons
selectedButtonColor primary Active page color

Example #

See the example app for a complete demo with all modes.

License #

MIT — see LICENSE.

4
likes
0
points
111
downloads

Publisher

verified publisheralmasum.dev

Weekly Downloads

Flutter pagination: generic page keys (int, cursor, offset), infinite scroll, load more button, grid view, slivers, numbered pagination, controlled mode, updateFetchPage for search/filter, built-in loading/error/empty states, pull-to-refresh, initialItems, pageSize auto-detection, totalItems tracking, header/footer params, skeleton loading builder, and testing utilities.

Repository (GitHub)
View/report issues

Topics

#pagination #infinite-scroll #lazy-loading #listview #gridview

License

unknown (license)

Dependencies

flutter, matcher

More

Packages that depend on flutter_pagination_pro