From e899d7678b5d54f3ce022e0a07a71b00e7f88ebf Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Tue, 25 Feb 2025 18:26:10 +0100 Subject: [PATCH 1/2] Start migrating to infinite scroll pagination v5 --- .../helpers/sourced_paging_controller.dart | 73 +++++++++---------- app/lib/pages/calendar/pending.dart | 4 +- app/lib/pages/events/page.dart | 55 +++++++------- app/pubspec.lock | 20 ++--- app/pubspec.yaml | 2 +- 5 files changed, 79 insertions(+), 75 deletions(-) diff --git a/app/lib/helpers/sourced_paging_controller.dart b/app/lib/helpers/sourced_paging_controller.dart index a5c92241b8f..a3cf34ba7ea 100644 --- a/app/lib/helpers/sourced_paging_controller.dart +++ b/app/lib/helpers/sourced_paging_controller.dart @@ -5,49 +5,46 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:flow_api/models/model.dart'; import 'package:flow_api/services/source.dart'; -class SourcedPagingController - extends PagingController, SourcedModel> { - final FlowCubit cubit; - final int pageSize; +const kDefaultPageSize = 50; - List get sources => cubit.getCurrentSources(); +typedef SourcedPagingController + = PagingController, SourcedModel>; - SourcedPagingController(this.cubit, {this.pageSize = 50}) - : super(firstPageKey: const SourcedModel("", -1)); - - PageRequestListener> addFetchListener( - Future?> Function(String, SourceService, int offset, int limit) - fetch) { - FutureOr listener(SourcedModel pageKey) async { - final isFirstPage = pageKey.model < 0; - var currentPageKey = pageKey; - if (isFirstPage) { - currentPageKey = SourcedModel(sources.first, 0); - } +SourcedPagingController createSourcedPagingController({ + required FlowCubit cubit, + int pageSize = kDefaultPageSize, + required Future?> Function( + String, SourceService, int offset, int limit) + fetch, +}) { + final sources = cubit.getCurrentSources(); + return SourcedPagingController( + fetchPage: (pageKey) async { final fetched = (await fetch( - currentPageKey.source, - cubit.getService(currentPageKey.source), - currentPageKey.model * pageSize, + pageKey.source, + cubit.getService(pageKey.source), + pageKey.model * pageSize, pageSize) ?? []) - .map((e) => SourcedModel(currentPageKey.source, e)) + .map((e) => SourcedModel(pageKey.source, e)) .toList(); - final index = sources.indexOf(currentPageKey.source); - final currentSource = isFirstPage ? sources.first : currentPageKey.source; - final keepSource = fetched.length >= pageSize; - final isLastSource = index >= sources.length - 1; - if (isLastSource && !keepSource) { - appendLastPage(fetched); - } else if (keepSource) { - appendPage( - fetched, SourcedModel(currentSource, currentPageKey.model + 1)); - } else { - final nextSource = sources[index + 1]; - appendPage(fetched, SourcedModel(nextSource, 0)); + return fetched; + }, + getNextPageKey: (state) { + final keys = state.keys?.lastOrNull; + if (keys == null) { + return SourcedModel(sources.first, 0); } - } - - addPageRequestListener(listener); - return listener; - } + final items = state.pages?.lastOrNull; + final isFinished = items == null || items.length < pageSize; + if (!isFinished) { + return SourcedModel(keys.source, keys.model + 1); + } + final index = sources.indexOf(keys.source); + if (index >= sources.length - 1 || index < 0) { + return null; + } + return SourcedModel(sources[index + 1], 0); + }, + ); } diff --git a/app/lib/pages/calendar/pending.dart b/app/lib/pages/calendar/pending.dart index b72c2d87900..cbcc349ab82 100644 --- a/app/lib/pages/calendar/pending.dart +++ b/app/lib/pages/calendar/pending.dart @@ -36,7 +36,9 @@ class _CalendarPendingViewState extends State { void initState() { super.initState(); _cubit = context.read(); - _controller = SourcedPagingController(_cubit); + _controller = createSourcedPagingController( + _cubit, + ); _controller.addFetchListener((source, service, offset, limit) async => service.calendarItem?.getCalendarItems( status: EventStatus.values diff --git a/app/lib/pages/events/page.dart b/app/lib/pages/events/page.dart index 7ba2b9cc380..9febc133930 100644 --- a/app/lib/pages/events/page.dart +++ b/app/lib/pages/events/page.dart @@ -155,31 +155,36 @@ class _EventsBodyViewState extends State { ), const SizedBox(height: 8), Expanded( - child: PagedListView( - pagingController: _controller, - builderDelegate: buildMaterialPagedDelegate>( - _controller, - (ctx, item, index) => Align( - alignment: Alignment.topCenter, - child: Container( - constraints: const BoxConstraints(maxWidth: 800), - child: Dismissible( - key: ValueKey('${item.model.id}@${item.source}'), - onDismissed: (direction) async { - await _flowCubit - .getService(item.source) - .event - ?.deleteEvent(item.model.id!); - _controller.itemList!.remove(item); - }, - background: Container( - color: Colors.red, - ), - child: EventTile( - flowCubit: _flowCubit, - pagingController: _controller, - source: item.source, - event: item.model, + child: PagingListener( + controller: _controller, + builder: (context, state, fetchNextPage) => PagedListView( + state: state, + fetchNextPage: fetchNextPage, + builderDelegate: + buildMaterialPagedDelegate>( + _controller, + (ctx, item, index) => Align( + alignment: Alignment.topCenter, + child: Container( + constraints: const BoxConstraints(maxWidth: 800), + child: Dismissible( + key: ValueKey('${item.model.id}@${item.source}'), + onDismissed: (direction) async { + await _flowCubit + .getService(item.source) + .event + ?.deleteEvent(item.model.id!); + _controller.refresh(); + }, + background: Container( + color: Colors.red, + ), + child: EventTile( + flowCubit: _flowCubit, + pagingController: _controller, + source: item.source, + event: item.model, + ), ), ), ), diff --git a/app/pubspec.lock b/app/pubspec.lock index 5033e876ee9..5d4a508c1af 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -262,10 +262,10 @@ packages: dependency: transitive description: name: dev_build - sha256: "5ed4fe61cead9fd561f13c7a05f6c11002ecc8ec6f5be33f7b9674f49f245434" + sha256: "43422b4e090a3bad7c1d612862bdf26c64e78a3624ecd780679df21384e8e91a" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.2+2" dynamic_color: dependency: "direct main" description: @@ -585,10 +585,10 @@ packages: dependency: "direct main" description: name: infinite_scroll_pagination - sha256: "4047eb8191e8b33573690922a9e995af64c3949dc87efc844f936b039ea279df" + sha256: ff5232d9066f664d31eee1b7dd0dedf79c71e7d8874567a7af5a51630e38b234 url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "5.0.0" intl: dependency: "direct main" description: @@ -756,18 +756,18 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "67eae327b1b0faf761964a1d2e5d323c797f3799db0e85aa232db8d9e922bc35" + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" url: "https://pub.dev" source: hosted - version: "8.2.1" + version: "8.3.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: "205ec83335c2ab9107bbba3f8997f9356d72ca3c715d2f038fc773d0366b4c76" + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.2.0" path: dependency: "direct main" description: @@ -1145,10 +1145,10 @@ packages: dependency: "direct main" description: name: sqlite3_flutter_libs - sha256: "57fafacd815c981735406215966ff7caaa8eab984b094f52e692accefcbd9233" + sha256: "7adb4cc96dc08648a5eb1d80a7619070796ca6db03901ff2b6dcb15ee30468f3" url: "https://pub.dev" source: hosted - version: "0.5.30" + version: "0.5.31" stack_trace: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 57d40d4f3c4..e46f62e07ff 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -46,7 +46,7 @@ dependencies: flutter_bloc: ^9.0.0 shared_preferences: ^2.2.3 collection: ^1.17.1 - infinite_scroll_pagination: ^4.0.0 + infinite_scroll_pagination: ^5.0.0 window_manager: ^0.4.3 package_info_plus: ^8.0.0 # Database From 55fbe040864279660ddd21383179a62b84236c51 Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Thu, 27 Feb 2025 23:09:28 +0100 Subject: [PATCH 2/2] Try to migrate one page --- app/lib/pages/calendar/pending.dart | 62 +++++++++++++++-------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/app/lib/pages/calendar/pending.dart b/app/lib/pages/calendar/pending.dart index cbcc349ab82..1a2f925b637 100644 --- a/app/lib/pages/calendar/pending.dart +++ b/app/lib/pages/calendar/pending.dart @@ -37,20 +37,19 @@ class _CalendarPendingViewState extends State { super.initState(); _cubit = context.read(); _controller = createSourcedPagingController( - _cubit, - ); - _controller.addFetchListener((source, service, offset, limit) async => - service.calendarItem?.getCalendarItems( - status: EventStatus.values - .where( - (element) => !widget.filter.hiddenStatuses.contains(element)) - .toList(), - search: widget.search, - pending: true, - offset: offset, - limit: limit, - resourceIds: widget.filter.resources, - )); + cubit: _cubit, + fetch: (source, service, offset, limit) async => + service.calendarItem?.getCalendarItems( + status: EventStatus.values + .where((element) => + !widget.filter.hiddenStatuses.contains(element)) + .toList(), + search: widget.search, + pending: true, + offset: offset, + limit: limit, + resourceIds: widget.filter.resources, + )); } @override @@ -82,23 +81,28 @@ class _CalendarPendingViewState extends State { const SizedBox(height: 8), Expanded( child: LayoutBuilder( - builder: (context, constraints) => PagedListView( - pagingController: _controller, - builderDelegate: buildMaterialPagedDelegate< - SourcedConnectedModel>( - _controller, - (context, item, index) { - return ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 1000), - child: CalendarListTile( - key: ValueKey('${item.source}@${item.main.id}'), - eventItem: item, - onRefresh: _controller.refresh, + builder: (context, constraints) => PagingListener( + controller: _controller, + builder: (context, state, fetchNextPage) { + return PagedListView( + state: state, + fetchNextPage: fetchNextPage, + builderDelegate: buildMaterialPagedDelegate< + SourcedConnectedModel>( + _controller, + (context, item, index) { + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 1000), + child: CalendarListTile( + key: ValueKey('${item.source}@${item.main.id}'), + eventItem: item, + onRefresh: _controller.refresh, + ), + ); + }, ), ); - }, - ), - ), + }), ), ), ],