In v5, the package decouples the PagingController from PagedLayoutBuilder and its descendants, to allow greater freedom in how a PagingState is managed. This is a large breaking change and will require refactoring in your code.
The package was upgraded to a newer modern flutter major version.
- Newly requires
dart: ">=3.4.0"andflutter: ">=3.0.0"for modern language features. - Newly depends on
collection: ">=1.15.0"for deep collection equality onPagingState. - Newly depends on
meta: ">=1.8.0"for annotations onPagingStateextension methods.
Since PagingController is now optional, it was changed to be more opinionated and easier to use.
Instead of adding PageRequestListener to your PagingController, like so:
final pagingController = PagingController<int, Photo>(firstPageKey: 1);
pagingController.addPageRequestListener(fetchPage);and manually updating the next page key:
pagingController.appendPage(newItems, nextPageKey);PagingController now directly takes and controls the fetching process:
late final pagingController = PagingController<int, Photo>(
getNextPageKey: (state) => state.lastPageIsEmpty ? null : state.nextIntPageKey,
fetchPage: (pageKey) => fetchPage(pageKey),
);Fetching the next page is handled via the fetchPage parameter.
The result is automatically merged with the current state. To indicate whether a page is the last page, getNextPageKey should return null on its next call.
If you have complicated custom logic for computing the next page key, consider writing your own Controller or extending the PagingController.
This fixes several issues of the past:
- Requests will now be actively deduplicated
- Refresh can now cancel previous requests
The PagingController can also be arbitrarily extended to include additional functionality that you might require. The source code explains how to structure new code.
Lastly, the various getter and setter methods previously featured to modify the state have been removed.
New getters have been added, however, setters have been left out since it should not be necessary to modify the state often.
One exception is the newly provided mapItems extension method, which can be used to modify the items in a convenient way, while retaining their page structure.
itemListandnextPageKeyproperties have been removed.pages,items,keys,error,hasNextPageandisLoadingextension getters as well asmapItemsto modify the items have been added.addPageRequestListenerwas removed. Use thefetchPageparameter of the constructor instead.appendPageandappendLastPagehave been removed. Use thecopyWithmethod of thePagingStateto update thepages,keysandhasNextPagefields.retryLastFailedRequestwas removed. You can simply callfetchNextPageto try again.invisibleItemsThresholdparameter has been removed. To configure theinvisibleItemsThresholdof a layout, use the corresponding parameter of itsPagedChildBuilderDelegate.
Because the PagingController is now independant, PagedLayoutBuilder and its subclasses no longer take a controller as a parameter like so:
PagedListView.builder(
pagingController: pagingController,
builderDelegate: PagedChildBuilderDelegate(
itemBuilder: (context, item, index) => ImageListTile(item),
),
),Instead, it is more agnostic:
PagedListView.builder(
state: state,
fetchNextPage: fetchNextPage,
builderDelegate: PagedChildBuilderDelegate(
itemBuilder: (context, item, index) => ImageListTile(item),
),
),Taking in a PagingState and a fetchNextPage function. fetchNextPage is a void function, and does not receive a page key.
This new design can be used in combination with any state management solution much more easily. A PagingController is no longer required. To continue using a PagingController for its convenience, you can connect it to any number of Paged Layouts via the PagingListener:
PagingListener(
controller: pagingController,
builder: (context, state, fetchNextPage) =>
PagedListView.builder(
state: state,
fetchNextPage: fetchNextPage,
builderDelegate: PagedChildBuilderDelegate(
itemBuilder: (context, item, index) => ImageListTile(item),
),
),
),It is highly recommended to directly store a PagingState inside of your preferred state management solution, instead of storing a PagingController, should you not wish to use the PagingController directly.
Examples of using a custom state management solution can be found in the example project.
- No longer features
pagingControllerparameter. Use thestateandfetchNextPageparameters instead. - Now uses
invisibleItemsThresholdfromPagedChildBuilderDelegateinstead ofPagingController.
The PagingState has been updated to be more flexible:
- It now includes a List of all keys,
keys, that have been fetched, each index corresponding to a page of items. - Instead of storing the next page key, it now includes a boolean
hasNextPageto indicate if there are more pages to fetch. - Lastly it now also includes a loading state, in
isLoading.
Because Items are now stored within pages, it is more difficult to modify the items directly.
To make this easier, a mapItems extension method has been added to modify the items by iterating over them.
Additionally, a filterItems extension method has been added to filter the items. This is useful for creating locally filtered computed states.
To allow for easy retrieval of the next page key, the lastPageIsEmpty and nextIntPageKey getters have been added.
The internals of PagingState have been restructured to allow directly extending it, without breaking its interface. You can see an example of this in the PagingStateBase class.
itemListhas been replaced bypages, which is List<List> instead of List. An extensionitemsgetter is provided to flatten the list.keysis a new field that stores all keys that have been fetched, each index corresponding to a page of items.erroris now type Object? instead of dynamic.nextPageKeywas removed. You can use thekeysfield to compute the next page andhasNextPageto determine if there are more pages.isLoadingis a new field that indicates if a request is currently in progress.mapItemsandfilterItemshave been added to modify the items in a convenient way.