Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version 5: Decouple PagingController #356

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
bc18c86
chore: upgrade to dart 3
clragon Oct 13, 2024
90d040f
refactor: move paging status to extension
clragon Oct 13, 2024
4d2cc57
fix: remove pubspec.lock
clragon Oct 13, 2024
206a874
refactor: rewrite page controller and paging state
clragon Oct 19, 2024
9b7a386
refactor: example search and grid
clragon Oct 21, 2024
f876fa8
fix: library imports
clragon Oct 21, 2024
e3a0156
refactor: make paging controller mutex public
clragon Oct 22, 2024
f9ec432
refactor: folder structure
clragon Oct 22, 2024
df577fe
refactor: state status logic
clragon Oct 22, 2024
462f8c0
refactor: simplify paged layout builder build method
clragon Oct 22, 2024
83ac28a
refactor: tests for new logic
clragon Dec 13, 2024
07dfe6d
chore: remove listenable listener
clragon Dec 13, 2024
4d76b8b
chore: move default widgets
clragon Dec 13, 2024
a9ca183
chore: update exports
clragon Dec 13, 2024
ce2689f
docs: update setstate example
clragon Dec 13, 2024
a5bebfa
docs: update diagram
clragon Jan 9, 2025
05b15e7
docs: move example markdown
clragon Jan 9, 2025
9af54bb
docs: add readme for example project
clragon Jan 9, 2025
4535f0d
feat: add staggered grid view export
clragon Jan 10, 2025
ee3a136
docs: update readme example
clragon Jan 10, 2025
5982287
docs: reformat changelog
clragon Jan 10, 2025
26cbab0
docs: update cookbook
clragon Jan 10, 2025
df09039
fix: rebuild during build when calling fetch next page
clragon Jan 27, 2025
1edeae5
fix: refresh cancelling previous call
clragon Jan 27, 2025
262781a
fix: invisibleItemsThreshold off-by-one
clragon Jan 28, 2025
245f5d6
refactor: improve PagingStateBase constructor
clragon Jan 28, 2025
6b12b3f
docs: add migration guide
clragon Jan 28, 2025
7e7b3ab
fix: paging controller tests
clragon Jan 29, 2025
b3e9356
feat: allow modifying state during fetch
clragon Jan 29, 2025
1fa050c
feat: add extension methods
clragon Jan 29, 2025
556249d
feat: add assert for constructing valid paging state
clragon Jan 29, 2025
6ce2040
fix: large invisible thresholds not triggering page request
clragon Feb 2, 2025
c26cd6f
fix: incorrectly calling fetch next page during build
clragon Feb 2, 2025
551ff9f
fix: animate transition between first page loading, error and empty
clragon Feb 2, 2025
51a31b3
feat: add item filter extension
clragon Feb 2, 2025
beaf421
docs: update migration guide
clragon Feb 2, 2025
e5a54d1
docs: update changelog
clragon Feb 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ build/
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

# flutter library
/pubspec.lock
/pubspec.lock
92 changes: 47 additions & 45 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
## UNRELEASED
### Added
- `PagingListener` widget to connect a `PagingController` to a `PagedLayoutBuilder`.

### Changed
- `PagingController` no longer has `addPageRequestListener` method and `firstPageKey` parameter. Use the `fetchPage` parameter of the constructor instead.
- `PagingController` no longer has the `itemList`, `error`, and `nextPageKey` getters and setters. All values are now stored in `PagingState`.
- `PagingController` no longer has the `appendPage` and `appendLastPage` methods. Use the `copyWith` method of `PagingState` to update its `pages`, `keys`, and `hasNextPage` fields.
- `PagingController` no longer has the `retryLastFailedRequest` method. You can simply call `fetchNextPage` to try again.
- `PagingController` no longer has the `invisibleItemsThreshold` field. It is now configured in `PagedChildBuilderDelegate`.
- `PagingController` now features getters matching the fields of `PagingState` as well as `mapItems` to modify the items.
- `PagedLayoutBuilder` no longer accepts `pagingController` as a parameter. It now takes `PagingState` and `fetchNextPage` instead.
- `PagingState` now uses `pages` (`List<List<ItemType>>`) instead of `itemList` (`List<ItemType>`). A new extension getter `items` is provided for flattening.
- `PagingState` now features `keys`, a list storing all fetched keys, and `hasNextPage` replacing `nextPageKey`.
- `PagingState` now includes `isLoading`, which tracks whether a request is in progress.
- `PagingState` now provides `error` as type `Object?` instead of `dynamic`.
- `PagingState` now includes `mapItems` and `filterItems` extension methods for modifying items conveniently.

### Fixed
- `PagingController` now deduplicates requests.
- `PagingController` refresh operations now cancel previous requests.
- Off-by-one error in `invisibleItemsThreshold` calculation.
- Failure to trigger page request when `invisibleItemsThreshold` is too large.
- Animating between states with `animateTransitions`.

## 4.1.0 - 2024-11-09
### Added
- [PagedSliverMasonryGrid](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagedSliverMasonryGrid-class.html).
Expand All @@ -8,7 +33,7 @@
### Removed
- `pubspec.lock` from version control.

## [4.0.0] - 2023-08-17
## 4.0.0 - 2023-08-17
### Added
- [PagedMasonryGridView](https://pub.dev/documentation/infinite_scroll_pagination/4.0.0/infinite_scroll_pagination/PagedMasonryGridView-class.html).
- [PagedPageView](https://pub.dev/documentation/infinite_scroll_pagination/4.0.0/infinite_scroll_pagination/PagedPageView-class.html).
Expand All @@ -17,19 +42,19 @@
### Changed
- Renames `PagedSliverBuilder` to [PagedLayoutBuilder](https://pub.dev/documentation/infinite_scroll_pagination/4.0.0/infinite_scroll_pagination/PagedLayoutBuilder-class.html).

## [3.2.0] - 2022-05-23
## 3.2.0 - 2022-05-23
### Changed
- Migrates to Flutter 3.

## [3.1.0] - 2021-07-04
## 3.1.0 - 2021-07-04
### Added
- [animated status transitions](https://pub.dev/packages/infinite_scroll_pagination/example#animating-status-transitions).

## [3.0.1+1] - 2021-05-23
## 3.0.1+1 - 2021-05-23
### Added
- [Flutter Favorite](https://flutter.dev/docs/development/packages-and-plugins/favorites) status to the README.

## [3.0.1] - 2021-03-08
## 3.0.1 - 2021-03-08
### Added
- New unit tests.

Expand All @@ -39,37 +64,37 @@
### Fixed
- Code formatting in `ListenableListener`.

## [3.0.0] - 2021-03-04
## 3.0.0 - 2021-03-04
### Changed
- Promotes null safety to stable release.
- Migrates example project to null safety.
- Migrates code samples to null safety.

## [3.0.0-nullsafety.0] - 2021-02-06
## 3.0.0-nullsafety.0 - 2021-02-06
### Changed
- Migrates to null safety.

## [2.3.0] - 2021-01-15
## 2.3.0 - 2021-01-15
### Added
- [alternative constructor](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagingController/PagingController.fromValue.html) to [PagingController](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagingController-class.html) receiving an initial [PagingState](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagingState-class.html).

### Changed
- Cookbook file name.
- LICENSE file.

## [2.2.4] - 2021-01-08
## 2.2.4 - 2021-01-08
### Fixed
- New page requests happening before the end of the current frame.

## [2.2.3] - 2020-12-14
## 2.2.3 - 2020-12-14
### Fixed
- Bug in which manually resetting to a previous page would stop requesting subsequent pages.

## [2.2.2] - 2020-11-04
## 2.2.2 - 2020-11-04
### Added
- Condition to avoid requesting the first page when there are preloaded items.

## [2.2.1] - 2020-10-21
## 2.2.1 - 2020-10-21
### Added
- `shrinkWrapFirstPageIndicators` property to [PagedSliverList](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagedSliverList-class.html), [PagedSliverGrid](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagedSliverGrid-class.html), and [PagedSliverBuilder](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagedSliverBuilder-class.html).

Expand All @@ -79,73 +104,50 @@
### Fixed
- Separator being displayed on completed lists.

## [2.2.0+1] - 2020-10-19
## 2.2.0+1 - 2020-10-19
### Changed
- Constraints the Flutter SDK dependency to a minimum version of 1.22.0.

## [2.2.0] - 2020-10-18
## 2.2.0 - 2020-10-18
### Added
- New constructor parameters from [ScrollView](https://api.flutter.dev/flutter/widgets/ScrollView-class.html) to [PagedListView](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagedListView-class.html) and [PagedGridView](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagedGridView-class.html).

## [2.1.0+1] - 2020-10-13
## 2.1.0+1 - 2020-10-13
### Added
- Link to [raywenderlich.com tutorial](https://www.raywenderlich.com/265121/infinite-scrolling-pagination-in-flutter).

### Changed
- Examples to async/await.

## [2.1.0] - 2020-10-10
## 2.1.0 - 2020-10-10
### Added
- [noMoreItemsIndicatorBuilder](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagedChildBuilderDelegate/noMoreItemsIndicatorBuilder.html) to [PagedChildBuilderDelegate](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagedChildBuilderDelegate-class.html).
- Properties to both grid widgets to let you choose whether to display the progress, error, and completed listing indicators as grid items or below the grid, as in the list widgets.

## [2.0.1] - 2020-10-03
## 2.0.1 - 2020-10-03
### Fixed
- [PagingController](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagingController-class.html) not calling its status listeners.

## [2.0.0] - 2020-10-02
## 2.0.0 - 2020-10-02
### Changed
- **BREAKING CHANGE**: Replaces [PagedDataSource](https://pub.dev/documentation/infinite_scroll_pagination/1.1.1/infinite_scroll_pagination/PagedDataSource-class.html) and [PagedStateChangeListener](https://pub.dev/documentation/infinite_scroll_pagination/1.1.1/infinite_scroll_pagination/PagedStateChangeListener-class.html) with [PagingController](https://pub.dev/documentation/infinite_scroll_pagination/latest/infinite_scroll_pagination/PagingController-class.html), favoring composition over inheritance.

## [1.1.1] - 2020-09-23
## 1.1.1 - 2020-09-23
### Removed
- Scroll from first page progress indicator, first page error indicator, and no items found indicator.

## [1.1.0] - 2020-09-18
## 1.1.0 - 2020-09-18
### Added
- [PagedStateChangeListener](https://pub.dev/documentation/infinite_scroll_pagination/1.1.0/infinite_scroll_pagination/PagedStateChangeListener-class.html).

## [1.0.0+2] - 2020-08-22
## 1.0.0+2 - 2020-08-22
### Added
- Documentation to `PagedDataSource` properties.

### Changed
- README images reference URL.

## [1.0.0+1] - 2020-08-22
## 1.0.0+1 - 2020-08-22
### Added
- Images to README.md.
- Initial release.

[4.0.0]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/3.2.0..4.0.0
[3.2.0]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/3.1.0..3.2.0
[3.1.0]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/3.0.1+1..3.1.0
[3.0.1+1]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/3.0.1..3.0.1+1
[3.0.1]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/3.0.0..3.0.1
[3.0.0]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/3.0.0-nullsafety.0..3.0.0
[3.0.0-nullsafety.0]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/2.3.0..3.0.0-nullsafety.0
[2.3.0]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/2.2.4..2.3.0
[2.2.4]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/2.2.3..2.2.4
[2.2.3]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/2.2.2..2.2.3
[2.2.2]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/2.2.1..2.2.2
[2.2.1]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/2.2.0+1..2.2.1
[2.2.0+1]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/2.2.0..2.2.0+1
[2.2.0]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/2.1.0+1..2.2.0
[2.1.0+1]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/2.1.0..2.1.0+1
[2.1.0]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/2.0.1..2.1.0
[2.0.1]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/2.0.0..2.0.1
[2.0.0]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/1.1.1..2.0.0
[1.1.1]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/1.1.0..1.1.1
[1.1.0]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/1.0.0+2..1.1.0
[1.0.0+2]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/1.0.0+1..1.0.0+2
[1.0.0+1]: https://github.com/EdsonBueno/infinite_scroll_pagination/compare/1.0.0..1.0.0+1
134 changes: 134 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# From v4 to v5

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.

## Dependencies

The package was upgraded to a newer modern flutter major version.

- Newly requires `dart: ">=3.4.0"` and `flutter: ">=3.0.0"` for modern language features.
- Newly depends on `collection: ">=1.15.0"` for deep collection equality on `PagingState`.
- Newly depends on `meta: ">=1.8.0"` for annotations on `PagingState` extension methods.

## PagingController

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:

```dart
final pagingController = PagingController<int, Photo>(firstPageKey: 1);
pagingController.addPageRequestListener(fetchPage);
```

and manually updating the next page key:

```dart
pagingController.appendPage(newItems, nextPageKey);
```

PagingController now directly takes and controls the fetching process:

```dart
late final pagingController = PagingController<int, Photo>(
getNextPageKey: (state) => (state.keys?.last ?? 0) + 1,
fetchPage: (pageKey) => fetchPage(pageKey),
);
```

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.

### API Changes

- `itemList` and `nextPageKey` properties have been removed.
- `pages`, `items`, `keys`, `error`, `hasNextPage` and `isLoading` extension getters as well as `mapItems` to modify the items have been added.
- `addPageRequestListener` was removed. Use the `fetchPage` parameter of the constructor instead.
- `appendPage` and `appendLastPage` have been removed. Use the `copyWith` method of the `PagingState` to update the `pages`, `keys` and `hasNextPage` fields.
- `retryLastFailedRequest` was removed. You can simply call `fetchNextPage` to try again.
- `invisibleItemsThreshold` parameter has been removed. To configure the `invisibleItemsThreshold` of a layout, use the corresponding parameter of its `PagedChildBuilderDelegate`.

## PagedLayoutBuilder

Because the PagingController is now independant, PagedLayoutBuilder and its subclasses no longer take a controller as a parameter like so:

```dart
PagedListView.builder(
pagingController: pagingController,
builderDelegate: PagedChildBuilderDelegate(
itemBuilder: (context, item, index) => ImageListTile(item),
),
),
```

Instead, it is more agnostic:

```dart
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:

```dart
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.

### API Changes

- No longer features `pagingController` parameter. Use the `state` and `fetchNextPage` parameters instead.
- Now uses `invisibleItemsThreshold` from `PagedChildBuilderDelegate` instead of `PagingController`.

## PagingState

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 `hasNextPage` to 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.

### API Changes

- `itemList` has been replaced by `pages`, which is List<List<ItemType>> instead of List<ItemType>. An extension `items` getter is provided to flatten the list.
- `keys` is a new field that stores all keys that have been fetched, each index corresponding to a page of items.
- `error` is now type Object? instead of dynamic.
- `nextPageKey` was removed. You can use the `keys` field to compute the next page and `hasNextPage` to determine if there are more pages.
- `isLoading` is a new field that indicates if a request is currently in progress.
- `mapItems` and `filterItems` have been added to modify the items in a convenient way.
Loading