From 4c1f819fa68efd3ba3acc47faccc6a341bc07d7e Mon Sep 17 00:00:00 2001 From: matkurban <3496354336@qq.com> Date: Sat, 12 Sep 2026 06:03:56 +0800 Subject: [PATCH] fix: resolve analyzer errors and bump to 2.0.0 - Fix local variable shadowing errors (controller / scopeContext) that broke compilation - Fix self-assignment of innerRefItem* fields in ChatScrollObserver.standby - Guard BuildContext usage across async gaps with mounted checks - Migrate to package:material_ui; require Dart ^3.13.0 / Flutter >=3.47.0 - Upgrade flutter_lints to ^6.0.0 in package and example, resolve all lints - Format all Dart sources with the Dart 3.13 formatter - Replace the outdated counter smoke test in example with a home page smoke test - Bump version to 2.0.0 and update CHANGELOG --- CHANGELOG.md | 18 + analysis_options.yaml | 3 + example/analysis_options.yaml | 9 + .../lib/common/route/navigation_service.dart | 17 +- example/lib/common/route/router_config.dart | 16 +- .../custom_scrollview_center_demo_page.dart | 41 +- .../custom_scrollview_demo_page.dart | 76 +- .../multi_sliver_demo_page.dart | 57 +- .../sliver_appbar_demo_page.dart | 76 +- .../gridview_ctx_demo_page.dart | 13 +- .../gridview_custom_demo_page.dart | 26 +- .../gridview_demo/gridview_demo_page.dart | 40 +- .../gridview_fixed_height_demo_page.dart | 35 +- .../horizontal_gridview_demo_page.dart | 18 +- .../sliver_grid_demo_page.dart | 78 +- example/lib/features/home/home_page.dart | 2 +- .../horizontal_listview_page.dart | 11 +- .../infinite_listview_page.dart | 24 +- .../listview_ctx_demo_page.dart | 11 +- .../listview_custom_demo_page.dart | 24 +- .../listview_demo/listview_demo_page.dart | 43 +- .../listview_dynamic_offset_page.dart | 30 +- .../listview_fixed_height_demo_page.dart | 38 +- .../sliver_list_demo_page.dart | 76 +- .../nested_scrollview_demo_page.dart | 156 +- ...d_scrollview_tab_bar_view_demo_header.dart | 14 +- ...r_view_demo_logic_floating_action_btn.dart | 4 +- ...view_tab_bar_view_demo_logic_observer.dart | 55 +- ...ar_view_demo_logic_scroll_type_switch.dart | 4 +- ...lview_tab_bar_view_demo_logic_tab_bar.dart | 6 +- ...ted_scrollview_tab_bar_view_demo_page.dart | 37 +- ...ed_scrollview_tab_bar_view_demo_state.dart | 2 +- ...tab_bar_view_demo_floating_action_btn.dart | 5 +- ..._tab_bar_view_demo_header_list_sliver.dart | 42 +- ..._tab_bar_view_demo_scroll_type_switch.dart | 5 +- ...crollview_tab_bar_view_demo_tab1_view.dart | 39 +- ...crollview_tab_bar_view_demo_tab2_view.dart | 44 +- ...crollview_tab_bar_view_demo_tab3_view.dart | 70 +- ...d_scrollview_tab_bar_view_demo_tabbar.dart | 5 +- .../pageview_demo/pageview_demo_page.dart | 88 +- .../pageview_parallax_item_listener_page.dart | 90 +- .../pageview_demo/pageview_parallax_page.dart | 69 +- .../scene/anchor_demo/anchor_page.dart | 13 +- .../anchor_demo/anchor_waterfall_page.dart | 14 +- .../scene/azlist_demo/azlist_cursor.dart | 8 +- .../scene/azlist_demo/azlist_index_bar.dart | 24 +- .../scene/azlist_demo/azlist_item_view.dart | 18 +- .../scene/azlist_demo/azlist_model.dart | 12 +- .../scene/azlist_demo/azlist_page.dart | 103 +- .../chat_demo/helper/chat_data_helper.dart | 12 +- .../scene/chat_demo/model/chat_model.dart | 5 +- .../scene/chat_demo/page/chat_gpt_page.dart | 19 +- .../scene/chat_demo/page/chat_page.dart | 86 +- .../chat_demo/widget/chat_item_widget.dart | 20 +- .../widget/chat_unread_tip_view.dart | 19 +- .../scene/detail/header/detail_header.dart | 23 +- .../detail/logic/detail_logic_list_view.dart | 18 +- .../detail/logic/detail_logic_nav_bar.dart | 15 +- .../model/detail_nav_bar_tab_model.dart | 5 +- .../scene/detail/page/detail_page.dart | 20 +- .../detail/state/detail_state_config.dart | 18 +- .../detail/state/detail_state_list_view.dart | 15 +- .../detail/state/detail_state_nav_bar.dart | 2 +- .../detail/widget/detail_config_view.dart | 22 +- .../widget/detail_list_item_wrapper.dart | 20 +- .../scene/detail/widget/detail_list_view.dart | 17 +- .../scene/detail/widget/detail_nav_bar.dart | 12 +- .../widget/list_item/detail_list_module1.dart | 27 +- .../widget/list_item/detail_list_module2.dart | 52 +- .../widget/list_item/detail_list_module3.dart | 7 +- .../widget/list_item/detail_list_module4.dart | 28 +- .../widget/list_item/detail_list_module5.dart | 7 +- .../widget/list_item/detail_list_module6.dart | 35 +- .../widget/list_item/detail_list_module7.dart | 16 +- .../widget/list_item/detail_list_module8.dart | 34 +- .../expandable_carousel_slider_demo.dart | 23 +- .../scene/image_tab_demo/image_tab_page.dart | 20 +- .../scrollview_form_demo_page.dart | 29 +- .../video_list_auto_play_page.dart | 17 +- .../widgets/video_widget.dart | 7 +- .../mixin/visibility_exposure_mixin.dart | 4 +- .../page/visibility_listview_page.dart | 19 +- .../page/visibility_scrollview_page.dart | 85 +- .../waterfall_flow_grid_item_view.dart | 12 +- .../waterfall_flow_page.dart | 62 +- .../waterfall_flow_swipe_view.dart | 7 +- .../waterfall_flow_type.dart | 6 +- .../waterfall_flow_fixed_height_page.dart | 53 +- example/lib/main.dart | 8 +- example/lib/utils/keyboard.dart | 2 +- example/lib/utils/snackbar.dart | 4 +- example/lib/widgets/animation.dart | 22 +- example/lib/widgets/sliver.dart | 12 +- example/pubspec.lock | 67 +- example/pubspec.yaml | 34 +- example/test/widget_test.dart | 19 +- lib/scrollview_observer.dart | 2 +- .../observe_displaying_child_model_mixin.dart | 12 +- .../models/observe_scroll_child_model.dart | 5 +- ...observer_handle_contexts_result_model.dart | 2 +- .../models/observer_index_position_model.dart | 2 +- lib/src/common/observer_controller.dart | 203 +- lib/src/common/observer_listener.dart | 4 +- .../common/observer_notification_result.dart | 8 +- lib/src/common/observer_typedef.dart | 20 +- lib/src/common/observer_widget.dart | 155 +- lib/src/common/observer_widget_scope.dart | 18 +- .../common/observer_widget_tag_manager.dart | 16 +- lib/src/common/typedefs.dart | 8 +- .../gridview/grid_observer_controller.dart | 17 +- .../grid_observer_notification_result.dart | 19 +- lib/src/gridview/grid_observer_view.dart | 117 +- ...idview_observe_displaying_child_model.dart | 13 +- .../models/gridview_observe_model.dart | 18 +- .../listview/list_observer_controller.dart | 17 +- .../list_observer_notification_result.dart | 19 +- lib/src/listview/list_observer_view.dart | 117 +- ...stview_observe_displaying_child_model.dart | 13 +- .../models/listview_observe_model.dart | 18 +- lib/src/notification.dart | 20 +- lib/src/observer_core.dart | 89 +- .../sliver_observer_observe_result_model.dart | 8 +- ...ewport_observe_displaying_child_model.dart | 2 +- .../models/sliver_viewport_observe_model.dart | 2 +- .../sliver/sliver_observer_controller.dart | 15 +- lib/src/sliver/sliver_observer_listener.dart | 4 +- .../sliver_observer_notification_result.dart | 19 +- lib/src/sliver/sliver_observer_view.dart | 173 +- .../chat/chat_observer_scroll_physics.dart | 18 +- .../chat_observer_scroll_physics_mixin.dart | 50 +- .../utils/src/chat/chat_scroll_observer.dart | 75 +- .../src/chat/chat_scroll_observer_model.dart | 2 +- lib/src/utils/src/extends.dart | 4 +- lib/src/utils/src/nested_scroll_util.dart | 18 +- lib/src/utils/src/observer_utils.dart | 39 +- lib/src/utils/src/slivers.dart | 16 +- pubspec.lock | 57 +- pubspec.yaml | 9 +- test/chat_scroll_observer_test.dart | 232 +- test/grid_observer_test.dart | 506 ++-- test/list_observer_test.dart | 674 +++-- test/observer_utils_test.dart | 9 +- test/sliver_observer_test.dart | 2165 ++++++++--------- 143 files changed, 3404 insertions(+), 4400 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 180372d..2e0a3ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## 2.0.0 +- Breaking Changes + - Require Dart SDK `^3.13.0` and Flutter `>=3.47.0`. + - Migrate from `package:flutter/material.dart` to `package:material_ui` and add it as a dependency. +- ObserverController + - Fix local variable shadowing errors of `controller` reported by the analyzer. + - Guard `BuildContext` usage across async gaps with `mounted` checks. +- ObserverWidget + - Fix local variable shadowing error of `scopeContext` reported by the analyzer. + - Guard `BuildContext` usage across async gaps with `mounted` checks. +- ChatScrollObserver + - Fix self-assignment of `innerRefItemIndex`, `innerRefItemIndexAfterUpdate` and `innerRefItemLayoutOffset` in `standby`. + - Guard `BuildContext` usage across async gaps with `mounted` checks. +- Others + - Upgrade `flutter_lints` to `^6.0.0` and resolve all analyzer issues in the package and example. + - Adopt super parameters, `base` class modifiers and remove leading underscores from local identifiers. + - Replace the outdated counter smoke test of the example with a home page smoke test. + ## 1.27.1 - ObserverController - Correct the scrolling clamped by an outdated `scrollExtent` by @LinXunFeng in [#150](https://github.com/fluttercandies/flutter_scrollview_observer/issues/150). diff --git a/analysis_options.yaml b/analysis_options.yaml index a5744c1..e46654b 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,3 +1,6 @@ +analyzer: + exclude: + - build/** include: package:flutter_lints/flutter.yaml # Additional information about this file can be found at diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 61b6c4d..5bee4ff 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -7,6 +7,15 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + exclude: + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** include: package:flutter_lints/flutter.yaml linter: diff --git a/example/lib/common/route/navigation_service.dart b/example/lib/common/route/navigation_service.dart index 34362cd..360a65e 100644 --- a/example/lib/common/route/navigation_service.dart +++ b/example/lib/common/route/navigation_service.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-02 15:49:58 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:go_router/go_router.dart'; class NavigationService { @@ -13,22 +13,13 @@ class NavigationService { static BuildContext get context => navigatorKey.currentState!.context; - static void push( - String location, { - Object? extra, - }) { - context.push( - location, - extra: extra, - ); + static void push(String location, {Object? extra}) { + context.push(location, extra: extra); } static void pop() => context.pop(); - static dynamic getParam( - String key, { - dynamic defaultValue, - }) { + static dynamic getParam(String key, {dynamic defaultValue}) { final state = routerState(); final pathParam = state.pathParameters[key]; if (pathParam != null) return pathParam; diff --git a/example/lib/common/route/router_config.dart b/example/lib/common/route/router_config.dart index 205bd34..df296db 100644 --- a/example/lib/common/route/router_config.dart +++ b/example/lib/common/route/router_config.dart @@ -5,7 +5,7 @@ */ import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:go_router/go_router.dart'; import 'package:scrollview_observer_example/common/route/route.dart'; import 'package:scrollview_observer_example/features/custom_scrollview/custom_scrollview_demo/custom_scrollview_center_demo_page.dart'; @@ -101,18 +101,13 @@ class MyRoute { static final routerConfig = GoRouter( routes: routes, initialLocation: MyPage.home, - observers: [ - observer, - ], + observers: [observer], navigatorKey: NavigationService.navigatorKey, debugLogDiagnostics: !kReleaseMode, ); static final List routes = [ - GoRoute( - path: MyPage.home, - builder: (context, state) => const HomePage(), - ), + GoRoute(path: MyPage.home, builder: (context, state) => const HomePage()), GoRoute( path: MyPage.listView, builder: (context, state) => const ListViewDemoPage(), @@ -221,10 +216,7 @@ class MyRoute { path: MyPage.imageTab, builder: (context, state) => const ImageTabPage(), ), - GoRoute( - path: MyPage.chat, - builder: (context, state) => const ChatPage(), - ), + GoRoute(path: MyPage.chat, builder: (context, state) => const ChatPage()), GoRoute( path: MyPage.chatGPT, builder: (context, state) => const ChatGPTPage(), diff --git a/example/lib/features/custom_scrollview/custom_scrollview_demo/custom_scrollview_center_demo_page.dart b/example/lib/features/custom_scrollview/custom_scrollview_demo/custom_scrollview_center_demo_page.dart index e565d6e..5c49d92 100644 --- a/example/lib/features/custom_scrollview/custom_scrollview_demo/custom_scrollview_center_demo_page.dart +++ b/example/lib/features/custom_scrollview/custom_scrollview_demo/custom_scrollview_center_demo_page.dart @@ -4,12 +4,12 @@ * @Date: 2024-03-11 21:08:23 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; class CustomScrollViewCenterDemoPage extends StatefulWidget { - const CustomScrollViewCenterDemoPage({Key? key}) : super(key: key); + const CustomScrollViewCenterDemoPage({super.key}); @override State createState() => @@ -49,10 +49,10 @@ class _CustomScrollViewCenterDemoPageState child: _buildScrollView(), sliverContexts: () { return [ - if (_sliverListCtx1 != null) _sliverListCtx1!, - if (_sliverListCtx2 != null) _sliverListCtx2!, - if (_sliverListCtx3 != null) _sliverListCtx3!, - if (_sliverListCtx4 != null) _sliverListCtx4!, + ?_sliverListCtx1, + ?_sliverListCtx2, + ?_sliverListCtx3, + ?_sliverListCtx4, ]; }, onObserveAll: (resultMap) { @@ -203,23 +203,20 @@ class _CustomScrollViewCenterDemoPageState Function(BuildContext)? onBuild, }) { return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - onBuild?.call(ctx); - final int itemIndex = index ~/ 2; - return Container( - height: (itemIndex % 2 == 0) ? 80 : 50, - color: color, - child: Center( - child: Text( - "index -- $index", - style: const TextStyle(color: Colors.white), - ), + delegate: SliverChildBuilderDelegate((ctx, index) { + onBuild?.call(ctx); + final int itemIndex = index ~/ 2; + return Container( + height: (itemIndex % 2 == 0) ? 80 : 50, + color: color, + child: Center( + child: Text( + "index -- $index", + style: const TextStyle(color: Colors.white), ), - ); - }, - childCount: 100, - ), + ), + ); + }, childCount: 100), ); } } diff --git a/example/lib/features/custom_scrollview/custom_scrollview_demo/custom_scrollview_demo_page.dart b/example/lib/features/custom_scrollview/custom_scrollview_demo/custom_scrollview_demo_page.dart index bd5d47e..2f03e1c 100644 --- a/example/lib/features/custom_scrollview/custom_scrollview_demo/custom_scrollview_demo_page.dart +++ b/example/lib/features/custom_scrollview/custom_scrollview_demo/custom_scrollview_demo_page.dart @@ -3,13 +3,13 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; class CustomScrollViewDemoPage extends StatefulWidget { - const CustomScrollViewDemoPage({Key? key}) : super(key: key); + const CustomScrollViewDemoPage({super.key}); @override State createState() => @@ -35,9 +35,7 @@ class _CustomScrollViewDemoPageState extends State { // Trigger an observation manually ambiguate(WidgetsBinding.instance)?.addPostFrameCallback((timeStamp) { - observerController.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - ); + observerController.dispatchOnceObserve(sliverContext: _sliverListCtx!); }); } @@ -55,10 +53,7 @@ class _CustomScrollViewDemoPageState extends State { controller: observerController, child: _buildScrollView(), sliverContexts: () { - return [ - if (_sliverListCtx != null) _sliverListCtx!, - if (_sliverGridCtx != null) _sliverGridCtx!, - ]; + return [?_sliverListCtx, ?_sliverGridCtx]; }, onObserveAll: (resultMap) { final model1 = resultMap[_sliverListCtx]; @@ -80,8 +75,9 @@ class _CustomScrollViewDemoPageState extends State { debugPrint('2 visible -- ${model2.visible}'); debugPrint('2 displaying -- ${model2.displayingChildIndexList}'); setState(() { - _hitIndexsForGrid = - model2.firstGroupChildList.map((e) => e.index).toList(); + _hitIndexsForGrid = model2.firstGroupChildList + .map((e) => e.index) + .toList(); }); } }, @@ -131,10 +127,7 @@ class _CustomScrollViewDemoPageState extends State { return CustomScrollView( controller: scrollController, // scrollDirection: Axis.horizontal, - slivers: [ - _buildSliverListView(), - _buildSliverGridView(), - ], + slivers: [_buildSliverListView(), _buildSliverGridView()], ); } @@ -162,25 +155,21 @@ class _CustomScrollViewDemoPageState extends State { // itemExtent: 100, // ); return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - _sliverListCtx ??= ctx; - return Container( - height: (index % 2 == 0) ? 80 : 50, - color: _hitIndexForCtx1 == index ? Colors.red : Colors.black12, - child: Center( - child: Text( - "index -- $index", - style: TextStyle( - color: - _hitIndexForCtx1 == index ? Colors.white : Colors.black, - ), + delegate: SliverChildBuilderDelegate((ctx, index) { + _sliverListCtx ??= ctx; + return Container( + height: (index % 2 == 0) ? 80 : 50, + color: _hitIndexForCtx1 == index ? Colors.red : Colors.black12, + child: Center( + child: Text( + "index -- $index", + style: TextStyle( + color: _hitIndexForCtx1 == index ? Colors.white : Colors.black, ), ), - ); - }, - childCount: 30, - ), + ), + ); + }, childCount: 30), ); } @@ -192,20 +181,15 @@ class _CustomScrollViewDemoPageState extends State { crossAxisSpacing: 10.0, childAspectRatio: 2.0, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - _sliverGridCtx ??= context; - return Container( - color: (_hitIndexsForGrid.contains(index)) - ? Colors.green - : Colors.blue[100], - child: Center( - child: Text('index -- $index'), - ), - ); - }, - childCount: 150, - ), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + _sliverGridCtx ??= context; + return Container( + color: (_hitIndexsForGrid.contains(index)) + ? Colors.green + : Colors.blue[100], + child: Center(child: Text('index -- $index')), + ); + }, childCount: 150), ); } } diff --git a/example/lib/features/custom_scrollview/custom_scrollview_demo/multi_sliver_demo_page.dart b/example/lib/features/custom_scrollview/custom_scrollview_demo/multi_sliver_demo_page.dart index 9dc87a4..0e2ce6f 100644 --- a/example/lib/features/custom_scrollview/custom_scrollview_demo/multi_sliver_demo_page.dart +++ b/example/lib/features/custom_scrollview/custom_scrollview_demo/multi_sliver_demo_page.dart @@ -4,7 +4,7 @@ * @Date: 2023-09-16 19:41:33 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; @@ -14,14 +14,11 @@ class MultiSliverDemoModel { final String tag; final List value; - const MultiSliverDemoModel({ - required this.tag, - required this.value, - }); + const MultiSliverDemoModel({required this.tag, required this.value}); } class MultiSliverDemoPage extends StatefulWidget { - const MultiSliverDemoPage({Key? key}) : super(key: key); + const MultiSliverDemoPage({super.key}); @override State createState() => _MultiSliverDemoPageState(); @@ -73,9 +70,7 @@ class _MultiSliverDemoPageState extends State { } /// To observe sliver items and handle scrollTo. - Widget _buildSliverItemObserver({ - required Widget child, - }) { + Widget _buildSliverItemObserver({required Widget child}) { return SliverViewObserver( controller: sliverItemObserverController, sliverContexts: () => itemSliverIndexCtxMap.values.toList(), @@ -84,12 +79,9 @@ class _MultiSliverDemoPageState extends State { } /// To observe which sliver is currently the first. - Widget _buildSliverObserver({ - required Widget child, - }) { + Widget _buildSliverObserver({required Widget child}) { return SliverViewObserver( // controller: sliverObserverController, - child: child, sliverContexts: () => sliverIndexCtxMap.values.toList(), triggerOnObserveType: ObserverTriggerOnObserveType.directly, dynamicLeadingOffset: () { @@ -112,7 +104,7 @@ class _MultiSliverDemoPageState extends State { // If the sliver is not visible, continue. final visible = (ctx.findRenderObject() as RenderSliver).geometry?.visible ?? - false; + false; if (!visible) continue; currentTabIndex = sectionIndex; break; @@ -120,6 +112,8 @@ class _MultiSliverDemoPageState extends State { if (currentTabIndex == null) return; updateTabBarIndex(currentTabIndex); }, + // controller: sliverObserverController, + child: child, ); } @@ -187,9 +181,7 @@ class _MultiSliverDemoPageState extends State { border: Border.all(width: 0.5), color: value == index ? Colors.amber : Colors.white, ), - child: Text( - modelList[index].tag, - ), + child: Text(modelList[index].tag), ); }, ), @@ -209,27 +201,20 @@ class _MultiSliverDemoPageState extends State { color: Colors.white, padding: const EdgeInsets.only(left: 12), alignment: Alignment.centerLeft, - child: Text( - modelList[mainIndex].tag, - ), + child: Text(modelList[mainIndex].tag), ), sliver: SliverFixedExtentList( itemExtent: 120, - delegate: SliverChildBuilderDelegate( - (context, index) { - // Save the context of SliverList. - itemSliverIndexCtxMap[mainIndex] = context; - return Container( - padding: const EdgeInsets.only(left: 12), - color: RandomTool.color(), - alignment: Alignment.centerLeft, - child: Text( - modelList[mainIndex].value[index], - ), - ); - }, - childCount: modelList[mainIndex].value.length, - ), + delegate: SliverChildBuilderDelegate((context, index) { + // Save the context of SliverList. + itemSliverIndexCtxMap[mainIndex] = context; + return Container( + padding: const EdgeInsets.only(left: 12), + color: RandomTool.color(), + alignment: Alignment.centerLeft, + child: Text(modelList[mainIndex].value[index]), + ); + }, childCount: modelList[mainIndex].value.length), ), ); resultWidget = SliverObserveContext( @@ -242,7 +227,7 @@ class _MultiSliverDemoPageState extends State { return resultWidget; } - updateTabBarIndex(int index) { + void updateTabBarIndex(int index) { if (index == tabCurrentSelectedIndex.value) return; tabCurrentSelectedIndex.value = index; } diff --git a/example/lib/features/custom_scrollview/sliver_appbar_demo/sliver_appbar_demo_page.dart b/example/lib/features/custom_scrollview/sliver_appbar_demo/sliver_appbar_demo_page.dart index ee3a9cd..16d30d1 100644 --- a/example/lib/features/custom_scrollview/sliver_appbar_demo/sliver_appbar_demo_page.dart +++ b/example/lib/features/custom_scrollview/sliver_appbar_demo/sliver_appbar_demo_page.dart @@ -4,13 +4,13 @@ * @Date: 2022-08-20 09:22:52 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; class SliverAppBarDemoPage extends StatefulWidget { - const SliverAppBarDemoPage({Key? key}) : super(key: key); + const SliverAppBarDemoPage({super.key}); @override State createState() => _SliverAppBarDemoPageState(); @@ -44,9 +44,7 @@ class _SliverAppBarDemoPageState extends State { // Trigger an observation manually ambiguate(WidgetsBinding.instance)?.addPostFrameCallback((timeStamp) { - observerController.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - ); + observerController.dispatchOnceObserve(sliverContext: _sliverListCtx!); }); } @@ -61,12 +59,8 @@ class _SliverAppBarDemoPageState extends State { return Scaffold( body: SliverViewObserver( controller: observerController, - child: _buildScrollView(), sliverContexts: () { - return [ - if (_sliverListCtx != null) _sliverListCtx!, - if (_sliverGridCtx != null) _sliverGridCtx!, - ]; + return [?_sliverListCtx, ?_sliverGridCtx]; }, autoTriggerObserveTypes: const [ ObserverAutoTriggerObserveType.scrollEnd, @@ -82,7 +76,8 @@ class _SliverAppBarDemoPageState extends State { debugPrint('1 firstChild.size -- ${model1.firstChild?.size}'); debugPrint('1 displaying -- ${model1.displayingChildIndexList}'); debugPrint( - '1 displaying -- index${model1.firstChild?.index} -- ${model1.firstChild?.displayPercentage}'); + '1 displaying -- index${model1.firstChild?.index} -- ${model1.firstChild?.displayPercentage}', + ); setState(() { _hitIndexForCtx1 = model1.firstChild?.index ?? 0; }); @@ -95,11 +90,13 @@ class _SliverAppBarDemoPageState extends State { debugPrint('2 visible -- ${model2.visible}'); debugPrint('2 displaying -- ${model2.displayingChildIndexList}'); setState(() { - _hitIndexsForGrid = - model2.firstGroupChildList.map((e) => e.index).toList(); + _hitIndexsForGrid = model2.firstGroupChildList + .map((e) => e.index) + .toList(); }); } }, + child: _buildScrollView(), ), floatingActionButton: Padding( padding: const EdgeInsets.all(15.0), @@ -172,25 +169,21 @@ class _SliverAppBarDemoPageState extends State { Widget _buildSliverListView() { return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - _sliverListCtx ??= ctx; - return Container( - height: (index % 2 == 0) ? 80 : 50, - color: _hitIndexForCtx1 == index ? Colors.red : Colors.black12, - child: Center( - child: Text( - "index -- $index", - style: TextStyle( - color: - _hitIndexForCtx1 == index ? Colors.white : Colors.black, - ), + delegate: SliverChildBuilderDelegate((ctx, index) { + _sliverListCtx ??= ctx; + return Container( + height: (index % 2 == 0) ? 80 : 50, + color: _hitIndexForCtx1 == index ? Colors.red : Colors.black12, + child: Center( + child: Text( + "index -- $index", + style: TextStyle( + color: _hitIndexForCtx1 == index ? Colors.white : Colors.black, ), ), - ); - }, - childCount: 20, - ), + ), + ); + }, childCount: 20), ); } @@ -202,20 +195,15 @@ class _SliverAppBarDemoPageState extends State { crossAxisSpacing: 10.0, childAspectRatio: 2.0, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - _sliverGridCtx ??= context; - return Container( - color: (_hitIndexsForGrid.contains(index)) - ? Colors.green - : Colors.blue[100], - child: Center( - child: Text('index -- $index'), - ), - ); - }, - childCount: 20, - ), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + _sliverGridCtx ??= context; + return Container( + color: (_hitIndexsForGrid.contains(index)) + ? Colors.green + : Colors.blue[100], + child: Center(child: Text('index -- $index')), + ); + }, childCount: 20), ); } diff --git a/example/lib/features/gridview/gridview_ctx_demo/gridview_ctx_demo_page.dart b/example/lib/features/gridview/gridview_ctx_demo/gridview_ctx_demo_page.dart index 8384297..266b742 100644 --- a/example/lib/features/gridview/gridview_ctx_demo/gridview_ctx_demo_page.dart +++ b/example/lib/features/gridview/gridview_ctx_demo/gridview_ctx_demo_page.dart @@ -3,12 +3,12 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; class GridViewCtxDemoPage extends StatefulWidget { - const GridViewCtxDemoPage({Key? key}) : super(key: key); + const GridViewCtxDemoPage({super.key}); @override State createState() => _GridViewCtxDemoPageState(); @@ -35,7 +35,7 @@ class _GridViewCtxDemoPageState extends State { appBar: AppBar(title: const Text("GridView")), body: GridViewObserver( sliverGridContexts: () { - return [if (_sliverGridViewContext != null) _sliverGridViewContext!]; + return [?_sliverGridViewContext]; }, onObserveAll: (resultMap) { final model = resultMap[_sliverGridViewContext]; @@ -45,7 +45,8 @@ class _GridViewCtxDemoPageState extends State { }); debugPrint( - 'firstGroupChildList -- ${model.firstGroupChildList.map((e) => e.index)}'); + 'firstGroupChildList -- ${model.firstGroupChildList.map((e) => e.index)}', + ); debugPrint('displaying -- ${model.displayingChildIndexList}'); }, child: _buildGridView(), @@ -74,9 +75,7 @@ class _GridViewCtxDemoPageState extends State { } return Container( color: (_hitIndexs.contains(index)) ? Colors.red : Colors.blue[100], - child: Center( - child: Text('index -- $index'), - ), + child: Center(child: Text('index -- $index')), ); }, itemCount: 50, diff --git a/example/lib/features/gridview/gridview_custom_demo/gridview_custom_demo_page.dart b/example/lib/features/gridview/gridview_custom_demo/gridview_custom_demo_page.dart index 199806f..eee4a04 100644 --- a/example/lib/features/gridview/gridview_custom_demo/gridview_custom_demo_page.dart +++ b/example/lib/features/gridview/gridview_custom_demo/gridview_custom_demo_page.dart @@ -5,13 +5,13 @@ */ // ignore: implementation_imports import 'package:extended_list/src/rendering/sliver_grid.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:loading_more_list/loading_more_list.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; class GridViewCustomDemoPage extends StatefulWidget { - const GridViewCustomDemoPage({Key? key}) : super(key: key); + const GridViewCustomDemoPage({super.key}); @override State createState() => _GridViewCustomDemoPageState(); @@ -24,9 +24,7 @@ class _GridViewCustomDemoPageState extends State { @override void initState() { - observerController = GridObserverController( - controller: scrollController, - ); + observerController = GridObserverController(controller: scrollController); super.initState(); } @@ -35,7 +33,6 @@ class _GridViewCustomDemoPageState extends State { return Scaffold( appBar: AppBar(title: const Text('Custom')), body: GridViewObserver( - child: _buildGridView(), controller: observerController, customTargetRenderSliverType: (renderObj) { // Here you tell the package what type of RenderObject it needs to observe. @@ -50,20 +47,17 @@ class _GridViewCustomDemoPageState extends State { // }, onObserve: (resultModel) { debugPrint( - 'firstChild.index -- ${resultModel.firstGroupChildList.map((e) => e.index)}'); + 'firstChild.index -- ${resultModel.firstGroupChildList.map((e) => e.index)}', + ); debugPrint('displaying -- ${resultModel.displayingChildIndexList}'); }, + child: _buildGridView(), ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.airline_stops_sharp), onPressed: () { - SnackBarUtil.showSnackBar( - context: context, - text: 'Jump to item 10', - ); - observerController.jumpTo( - index: 10, - ); + SnackBarUtil.showSnackBar(context: context, text: 'Jump to item 10'); + observerController.jumpTo(index: 10); }, ), ); @@ -81,9 +75,7 @@ class _GridViewCustomDemoPageState extends State { } return Container( color: Colors.cyan, - child: ListTile( - title: Text('index - $index'), - ), + child: ListTile(title: Text('index - $index')), ); }, gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( diff --git a/example/lib/features/gridview/gridview_demo/gridview_demo_page.dart b/example/lib/features/gridview/gridview_demo/gridview_demo_page.dart index b9e9a15..045f1f1 100644 --- a/example/lib/features/gridview/gridview_demo/gridview_demo_page.dart +++ b/example/lib/features/gridview/gridview_demo/gridview_demo_page.dart @@ -3,13 +3,13 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; class GridViewDemoPage extends StatefulWidget { - const GridViewDemoPage({Key? key}) : super(key: key); + const GridViewDemoPage({super.key}); @override State createState() => _GridViewDemoPageState(); @@ -25,8 +25,9 @@ class _GridViewDemoPageState extends State { List _hitIndexs = [0, 1]; - ScrollController scrollController = - ScrollController(initialScrollOffset: _leadingPadding); + ScrollController scrollController = ScrollController( + initialScrollOffset: _leadingPadding, + ); late GridObserverController observerController; @@ -37,14 +38,12 @@ class _GridViewDemoPageState extends State { observerController = GridObserverController(controller: scrollController); // Trigger an observation manually - ambiguate(WidgetsBinding.instance)?.endOfFrame.then( - (_) { - if (mounted) { - // After layout - observerController.dispatchOnceObserve(); - } - }, - ); + ambiguate(WidgetsBinding.instance)?.endOfFrame.then((_) { + if (mounted) { + // After layout + observerController.dispatchOnceObserve(); + } + }); } @override @@ -66,7 +65,8 @@ class _GridViewDemoPageState extends State { }); debugPrint( - 'firstGroupChildList -- ${model.firstGroupChildList.map((e) => e.index)}'); + 'firstGroupChildList -- ${model.firstGroupChildList.map((e) => e.index)}', + ); debugPrint('displaying -- ${model.displayingChildIndexList}'); }, child: _buildGridView(), @@ -74,14 +74,8 @@ class _GridViewDemoPageState extends State { floatingActionButton: FloatingActionButton( child: const Icon(Icons.airline_stops_outlined), onPressed: () { - SnackBarUtil.showSnackBar( - context: context, - text: 'Jump to item 49', - ); - observerController.jumpTo( - index: 49, - padding: _padding, - ); + SnackBarUtil.showSnackBar(context: context, text: 'Jump to item 49'); + observerController.jumpTo(index: 49, padding: _padding); // observerController.animateTo( // index: 49, // duration: const Duration(seconds: 1), @@ -104,9 +98,7 @@ class _GridViewDemoPageState extends State { itemBuilder: (context, index) { return Container( color: (_hitIndexs.contains(index)) ? Colors.red : Colors.blue[100], - child: Center( - child: Text('index -- $index'), - ), + child: Center(child: Text('index -- $index')), ); }, itemCount: 50, diff --git a/example/lib/features/gridview/gridview_fixed_height_demo/gridview_fixed_height_demo_page.dart b/example/lib/features/gridview/gridview_fixed_height_demo/gridview_fixed_height_demo_page.dart index 2a9f8e6..aba37cb 100644 --- a/example/lib/features/gridview/gridview_fixed_height_demo/gridview_fixed_height_demo_page.dart +++ b/example/lib/features/gridview/gridview_fixed_height_demo/gridview_fixed_height_demo_page.dart @@ -3,13 +3,13 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; class GridViewFixedHeightDemoPage extends StatefulWidget { - const GridViewFixedHeightDemoPage({Key? key}) : super(key: key); + const GridViewFixedHeightDemoPage({super.key}); @override State createState() => @@ -29,8 +29,9 @@ class _GridViewFixedHeightDemoPageState List _hitIndexs = [0, 1]; - ScrollController scrollController = - ScrollController(initialScrollOffset: _leadingPadding); + ScrollController scrollController = ScrollController( + initialScrollOffset: _leadingPadding, + ); late GridObserverController observerController; @@ -41,14 +42,12 @@ class _GridViewFixedHeightDemoPageState observerController = GridObserverController(controller: scrollController); // Trigger an observation manually - ambiguate(WidgetsBinding.instance)?.endOfFrame.then( - (_) { - if (mounted) { - // After layout - observerController.dispatchOnceObserve(); - } - }, - ); + ambiguate(WidgetsBinding.instance)?.endOfFrame.then((_) { + if (mounted) { + // After layout + observerController.dispatchOnceObserve(); + } + }); } @override @@ -70,7 +69,8 @@ class _GridViewFixedHeightDemoPageState }); debugPrint( - 'firstGroupChildList -- ${model.firstGroupChildList.map((e) => e.index)}'); + 'firstGroupChildList -- ${model.firstGroupChildList.map((e) => e.index)}', + ); debugPrint('displaying -- ${model.displayingChildIndexList}'); }, child: _buildGridView(), @@ -78,10 +78,7 @@ class _GridViewFixedHeightDemoPageState floatingActionButton: FloatingActionButton( child: const Icon(Icons.airline_stops_outlined), onPressed: () { - SnackBarUtil.showSnackBar( - context: context, - text: 'Jump to item 21', - ); + SnackBarUtil.showSnackBar(context: context, text: 'Jump to item 21'); observerController.jumpTo( index: 21, padding: _padding, @@ -104,9 +101,7 @@ class _GridViewFixedHeightDemoPageState itemBuilder: (context, index) { return Container( color: (_hitIndexs.contains(index)) ? Colors.red : Colors.blue[100], - child: Center( - child: Text('index -- $index'), - ), + child: Center(child: Text('index -- $index')), ); }, itemCount: 50, diff --git a/example/lib/features/gridview/horizontal_gridview_demo/horizontal_gridview_demo_page.dart b/example/lib/features/gridview/horizontal_gridview_demo/horizontal_gridview_demo_page.dart index ddb9922..e748a6f 100644 --- a/example/lib/features/gridview/horizontal_gridview_demo/horizontal_gridview_demo_page.dart +++ b/example/lib/features/gridview/horizontal_gridview_demo/horizontal_gridview_demo_page.dart @@ -3,13 +3,13 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; class HorizontalGridViewDemoPage extends StatefulWidget { - const HorizontalGridViewDemoPage({Key? key}) : super(key: key); + const HorizontalGridViewDemoPage({super.key}); @override State createState() => @@ -28,8 +28,9 @@ class _HorizontalGridViewDemoPageState List _hitIndexs = []; - ScrollController scrollController = - ScrollController(initialScrollOffset: _leadingPadding); + ScrollController scrollController = ScrollController( + initialScrollOffset: _leadingPadding, + ); late GridObserverController observerController; @@ -61,7 +62,7 @@ class _HorizontalGridViewDemoPageState body: GridViewObserver( controller: observerController, sliverGridContexts: () { - return [if (_sliverGridViewContext != null) _sliverGridViewContext!]; + return [?_sliverGridViewContext]; }, autoTriggerObserveTypes: const [ ObserverAutoTriggerObserveType.scrollEnd, @@ -77,7 +78,8 @@ class _HorizontalGridViewDemoPageState }); debugPrint( - 'firstGroupChildList -- ${model.firstGroupChildList.map((e) => e.index)}'); + 'firstGroupChildList -- ${model.firstGroupChildList.map((e) => e.index)}', + ); debugPrint('displaying -- ${model.displayingChildIndexList}'); }, child: _buildGridView(), @@ -128,9 +130,7 @@ class _HorizontalGridViewDemoPageState } return Container( color: (_hitIndexs.contains(index)) ? Colors.red : Colors.blue[100], - child: Center( - child: Text('index -- $index'), - ), + child: Center(child: Text('index -- $index')), ); }, itemCount: 100, diff --git a/example/lib/features/gridview/sliver_grid_demo/sliver_grid_demo_page.dart b/example/lib/features/gridview/sliver_grid_demo/sliver_grid_demo_page.dart index 1a6802e..4220628 100644 --- a/example/lib/features/gridview/sliver_grid_demo/sliver_grid_demo_page.dart +++ b/example/lib/features/gridview/sliver_grid_demo/sliver_grid_demo_page.dart @@ -3,12 +3,12 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; class SliverGridViewDemoPage extends StatefulWidget { - const SliverGridViewDemoPage({Key? key}) : super(key: key); + const SliverGridViewDemoPage({super.key}); @override State createState() => _SliverGridViewDemoPageState(); @@ -37,33 +37,34 @@ class _SliverGridViewDemoPageState extends State { appBar: AppBar(title: const Text("GridView")), body: GridViewObserver( sliverGridContexts: () { - return [ - if (_sliverGridViewContext1 != null) _sliverGridViewContext1!, - if (_sliverGridViewContext2 != null) _sliverGridViewContext2! - ]; + return [?_sliverGridViewContext1, ?_sliverGridViewContext2]; }, onObserveAll: (resultMap) { final model1 = resultMap[_sliverGridViewContext1]; if (model1 != null && model1.visible) { setState(() { - _hitIndexs1 = - model1.firstGroupChildList.map((e) => e.index).toList(); + _hitIndexs1 = model1.firstGroupChildList + .map((e) => e.index) + .toList(); }); debugPrint( - '1 -- firstGroupChildList -- ${model1.firstGroupChildList.map((e) => e.index)}'); + '1 -- firstGroupChildList -- ${model1.firstGroupChildList.map((e) => e.index)}', + ); debugPrint('1 -- displaying -- ${model1.displayingChildIndexList}'); } final model2 = resultMap[_sliverGridViewContext2]; if (model2 != null && model2.visible) { setState(() { - _hitIndexs2 = - model2.firstGroupChildList.map((e) => e.index).toList(); + _hitIndexs2 = model2.firstGroupChildList + .map((e) => e.index) + .toList(); }); debugPrint( - '2 -- firstGroupChildList -- ${model2.firstGroupChildList.map((e) => e.index)}'); + '2 -- firstGroupChildList -- ${model2.firstGroupChildList.map((e) => e.index)}', + ); debugPrint('2 -- displaying -- ${model2.displayingChildIndexList}'); } }, @@ -74,30 +75,21 @@ class _SliverGridViewDemoPageState extends State { Widget _buildGridView() { return CustomScrollView( - slivers: [ - _buildSliverGridView1(), - _buildSliverGridView2(), - ], + slivers: [_buildSliverGridView1(), _buildSliverGridView2()], ); } Widget _buildSliverGridView1() { return SliverGrid( - delegate: SliverChildBuilderDelegate( - (context, index) { - if (_sliverGridViewContext1 != context) { - _sliverGridViewContext1 = context; - } - return Container( - color: - (_hitIndexs1.contains(index)) ? Colors.red : Colors.blue[100], - child: Center( - child: Text('index -- $index'), - ), - ); - }, - childCount: 50, - ), + delegate: SliverChildBuilderDelegate((context, index) { + if (_sliverGridViewContext1 != context) { + _sliverGridViewContext1 = context; + } + return Container( + color: (_hitIndexs1.contains(index)) ? Colors.red : Colors.blue[100], + child: Center(child: Text('index -- $index')), + ); + }, childCount: 50), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 2, @@ -108,21 +100,15 @@ class _SliverGridViewDemoPageState extends State { Widget _buildSliverGridView2() { return SliverGrid( - delegate: SliverChildBuilderDelegate( - (context, index) { - if (_sliverGridViewContext2 != context) { - _sliverGridViewContext2 = context; - } - return Container( - color: - (_hitIndexs2.contains(index)) ? Colors.red : Colors.blue[100], - child: Center( - child: Text('index -- $index'), - ), - ); - }, - childCount: 50, - ), + delegate: SliverChildBuilderDelegate((context, index) { + if (_sliverGridViewContext2 != context) { + _sliverGridViewContext2 = context; + } + return Container( + color: (_hitIndexs2.contains(index)) ? Colors.red : Colors.blue[100], + child: Center(child: Text('index -- $index')), + ); + }, childCount: 50), gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 140.0, childAspectRatio: 0.6, diff --git a/example/lib/features/home/home_page.dart b/example/lib/features/home/home_page.dart index 238d1b0..1d155be 100644 --- a/example/lib/features/home/home_page.dart +++ b/example/lib/features/home/home_page.dart @@ -4,7 +4,7 @@ * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/common/route/route.dart'; class HomePage extends StatelessWidget { diff --git a/example/lib/features/listview/horizontal_listview_demo/horizontal_listview_page.dart b/example/lib/features/listview/horizontal_listview_demo/horizontal_listview_page.dart index da6ed84..e3f3287 100644 --- a/example/lib/features/listview/horizontal_listview_demo/horizontal_listview_page.dart +++ b/example/lib/features/listview/horizontal_listview_demo/horizontal_listview_page.dart @@ -3,12 +3,12 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; class HorizontalListViewPage extends StatefulWidget { - const HorizontalListViewPage({Key? key}) : super(key: key); + const HorizontalListViewPage({super.key}); @override State createState() => _HorizontalListViewPageState(); @@ -36,7 +36,7 @@ class _HorizontalListViewPageState extends State { body: ListViewObserver( child: _buildListView(), sliverListContexts: () { - return [if (_sliverListViewContext != null) _sliverListViewContext!]; + return [?_sliverListViewContext]; }, onObserveAll: (resultMap) { final model = resultMap[_sliverListViewContext]; @@ -84,9 +84,6 @@ class _HorizontalListViewPageState extends State { } Container _buildSeparatorView() { - return Container( - color: Colors.white, - width: 5, - ); + return Container(color: Colors.white, width: 5); } } diff --git a/example/lib/features/listview/infinite_listview_demo/infinite_listview_page.dart b/example/lib/features/listview/infinite_listview_demo/infinite_listview_page.dart index adc14be..1e58fb1 100644 --- a/example/lib/features/listview/infinite_listview_demo/infinite_listview_page.dart +++ b/example/lib/features/listview/infinite_listview_demo/infinite_listview_page.dart @@ -6,11 +6,11 @@ import 'dart:math'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; class InfiniteListViewPage extends StatefulWidget { - const InfiniteListViewPage({Key? key}) : super(key: key); + const InfiniteListViewPage({super.key}); @override State createState() => _InfiniteListViewPageState(); @@ -42,7 +42,7 @@ class _InfiniteListViewPageState extends State { late ChatScrollObserver chatObserver; - handleLoadMoreForHeader() async { + Future handleLoadMoreForHeader() async { if (isLoadingForHeader) return; // loading isLoadingForHeader = true; int initIndex = 0; @@ -69,7 +69,7 @@ class _InfiniteListViewPageState extends State { isLoadingForHeader = false; } - handleLoadMoreForFooter() async { + Future handleLoadMoreForFooter() async { if (isLoadingForFooter) return; // loading isLoadingForFooter = true; int initIndex = 0; @@ -81,9 +81,7 @@ class _InfiniteListViewPageState extends State { dataSource.addAll( List.generate(generateCount, (index) => index + initIndex + 1), ); - itemHeights.addAll( - List.generate(generateCount, (_) => randomItemHeight), - ); + itemHeights.addAll(List.generate(generateCount, (_) => randomItemHeight)); }); isLoadingForFooter = false; } @@ -115,7 +113,8 @@ class _InfiniteListViewPageState extends State { child: _buildListView(), onObserve: (result) { debugPrint( - 'displaying -- ${result.displayingChildIndexList.map((i) => dataSource[i])}'); + 'displaying -- ${result.displayingChildIndexList.map((i) => dataSource[i])}', + ); if (result.firstChild?.index == 0) { final firstChildLeadingMarginToViewport = @@ -161,18 +160,13 @@ class _InfiniteListViewPageState extends State { child: Center( child: Text( "index -- ${dataSource[index]}", - style: const TextStyle( - color: Colors.black, - ), + style: const TextStyle(color: Colors.black), ), ), ); } Widget _buildSeparatorView() { - return Container( - color: Colors.white, - width: 5, - ); + return Container(color: Colors.white, width: 5); } } diff --git a/example/lib/features/listview/listview_ctx_demo/listview_ctx_demo_page.dart b/example/lib/features/listview/listview_ctx_demo/listview_ctx_demo_page.dart index 0a5aea6..860d723 100644 --- a/example/lib/features/listview/listview_ctx_demo/listview_ctx_demo_page.dart +++ b/example/lib/features/listview/listview_ctx_demo/listview_ctx_demo_page.dart @@ -3,12 +3,12 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; class ListViewCtxDemoPage extends StatefulWidget { - const ListViewCtxDemoPage({Key? key}) : super(key: key); + const ListViewCtxDemoPage({super.key}); @override State createState() => _ListViewCtxDemoPageState(); @@ -36,7 +36,7 @@ class _ListViewCtxDemoPageState extends State { body: ListViewObserver( child: _buildListView(), sliverListContexts: () { - return [if (_sliverListViewContext != null) _sliverListViewContext!]; + return [?_sliverListViewContext]; }, onObserveAll: (resultMap) { final model = resultMap[_sliverListViewContext]; @@ -97,9 +97,6 @@ class _ListViewCtxDemoPageState extends State { } Container _buildSeparatorView() { - return Container( - color: Colors.white, - height: 5, - ); + return Container(color: Colors.white, height: 5); } } diff --git a/example/lib/features/listview/listview_custom_demo/listview_custom_demo_page.dart b/example/lib/features/listview/listview_custom_demo/listview_custom_demo_page.dart index bed3274..064891b 100644 --- a/example/lib/features/listview/listview_custom_demo/listview_custom_demo_page.dart +++ b/example/lib/features/listview/listview_custom_demo/listview_custom_demo_page.dart @@ -5,13 +5,13 @@ */ // ignore: implementation_imports import 'package:extended_list/src/rendering/sliver_list.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:loading_more_list/loading_more_list.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; class ListViewCustomDemoPage extends StatefulWidget { - const ListViewCustomDemoPage({Key? key}) : super(key: key); + const ListViewCustomDemoPage({super.key}); @override State createState() => _ListViewCustomDemoPageState(); @@ -24,9 +24,7 @@ class _ListViewCustomDemoPageState extends State { @override void initState() { - observerController = ListObserverController( - controller: scrollController, - ); + observerController = ListObserverController(controller: scrollController); super.initState(); } @@ -35,7 +33,6 @@ class _ListViewCustomDemoPageState extends State { return Scaffold( appBar: AppBar(title: const Text('Custom')), body: ListViewObserver( - child: _buildListView(), controller: observerController, customTargetRenderSliverType: (renderObj) { // Here you tell the package what type of RenderObject it needs to observe. @@ -52,18 +49,13 @@ class _ListViewCustomDemoPageState extends State { debugPrint('firstChild.index -- ${resultModel.firstChild?.index}'); debugPrint('displaying -- ${resultModel.displayingChildIndexList}'); }, + child: _buildListView(), ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.airline_stops_sharp), onPressed: () { - SnackBarUtil.showSnackBar( - context: context, - text: 'Jump to row 10', - ); - observerController.jumpTo( - index: 10, - isFixedHeight: true, - ); + SnackBarUtil.showSnackBar(context: context, text: 'Jump to row 10'); + observerController.jumpTo(index: 10, isFixedHeight: true); }, ), ); @@ -79,9 +71,7 @@ class _ListViewCustomDemoPageState extends State { observerController.sliverContexts.first != context)) { observerController.reattach(); } - return ListTile( - title: Text('index - $index'), - ); + return ListTile(title: Text('index - $index')); }, sourceList: SourceList(), ), diff --git a/example/lib/features/listview/listview_demo/listview_demo_page.dart b/example/lib/features/listview/listview_demo/listview_demo_page.dart index a3b2538..7735d62 100644 --- a/example/lib/features/listview/listview_demo/listview_demo_page.dart +++ b/example/lib/features/listview/listview_demo/listview_demo_page.dart @@ -3,13 +3,13 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; class ListViewDemoPage extends StatefulWidget { - const ListViewDemoPage({Key? key}) : super(key: key); + const ListViewDemoPage({super.key}); @override State createState() => _ListViewDemoPageState(); @@ -24,8 +24,9 @@ class _ListViewDemoPageState extends State { ); int _hitIndex = 0; - ScrollController scrollController = - ScrollController(initialScrollOffset: _leadingPadding); + ScrollController scrollController = ScrollController( + initialScrollOffset: _leadingPadding, + ); late ListObserverController observerController; @@ -40,14 +41,12 @@ class _ListViewDemoPageState extends State { ); // Trigger an observation manually - ambiguate(WidgetsBinding.instance)?.endOfFrame.then( - (_) { - // After layout - if (mounted) { - observerController.dispatchOnceObserve(); - } - }, - ); + ambiguate(WidgetsBinding.instance)?.endOfFrame.then((_) { + // After layout + if (mounted) { + observerController.dispatchOnceObserve(); + } + }); } @override @@ -61,7 +60,6 @@ class _ListViewDemoPageState extends State { return Scaffold( appBar: AppBar(title: const Text("ListView")), body: ListViewObserver( - child: _buildListView(), autoTriggerObserveTypes: const [ ObserverAutoTriggerObserveType.scrollEnd, ], @@ -74,25 +72,21 @@ class _ListViewDemoPageState extends State { for (var item in resultModel.displayingChildModelList) { debugPrint( - 'item - ${item.index} - ${item.leadingMarginToViewport} - ${item.trailingMarginToViewport}'); + 'item - ${item.index} - ${item.leadingMarginToViewport} - ${item.trailingMarginToViewport}', + ); } setState(() { _hitIndex = resultModel.firstChild?.index ?? 0; }); }, + child: _buildListView(), ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.airline_stops_outlined), onPressed: () { - SnackBarUtil.showSnackBar( - context: context, - text: 'Jump to row 50', - ); - observerController.jumpTo( - index: 50, - padding: _padding, - ); + SnackBarUtil.showSnackBar(context: context, text: 'Jump to row 50'); + observerController.jumpTo(index: 50, padding: _padding); // observerController.animateTo( // index: 50, // duration: const Duration(seconds: 1), @@ -144,9 +138,6 @@ class _ListViewDemoPageState extends State { } Container _buildSeparatorView() { - return Container( - color: Colors.white, - height: 5, - ); + return Container(color: Colors.white, height: 5); } } diff --git a/example/lib/features/listview/listview_dynamic_offset/listview_dynamic_offset_page.dart b/example/lib/features/listview/listview_dynamic_offset/listview_dynamic_offset_page.dart index fb3e4e8..c450ec9 100644 --- a/example/lib/features/listview/listview_dynamic_offset/listview_dynamic_offset_page.dart +++ b/example/lib/features/listview/listview_dynamic_offset/listview_dynamic_offset_page.dart @@ -3,12 +3,12 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; class ListViewDynamicOffsetPage extends StatefulWidget { - const ListViewDynamicOffsetPage({Key? key}) : super(key: key); + const ListViewDynamicOffsetPage({super.key}); @override State createState() => @@ -58,9 +58,7 @@ class _ListViewDynamicOffsetPageState extends State { ListViewObserver( child: _buildListView(), sliverListContexts: () { - return [ - if (_sliverListViewContext != null) _sliverListViewContext! - ]; + return [?_sliverListViewContext]; }, dynamicLeadingOffset: () { if (_navBgAlpha < 1) { @@ -85,10 +83,7 @@ class _ListViewDynamicOffsetPageState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - SafeArea( - bottom: false, - child: _buildNavContentWidget(context), - ) + SafeArea(bottom: false, child: _buildNavContentWidget(context)), ], ), ), @@ -130,10 +125,7 @@ class _ListViewDynamicOffsetPageState extends State { } Container _buildSeparatorView() { - return Container( - color: Colors.white, - height: 5, - ); + return Container(color: Colors.white, height: 5); } Widget _buildNavContentWidget(BuildContext context) { @@ -172,17 +164,17 @@ class _ListViewDynamicOffsetPageState extends State { void _pageDidScroll() { final offset = _pageController.offset; - double _newNavBgAlpha = 0; + double newNavBgAlpha = 0; if (offset < 0) { - _newNavBgAlpha = 0; + newNavBgAlpha = 0; } else if (offset >= _navContentHeight) { - _newNavBgAlpha = 1; + newNavBgAlpha = 1; } else { - _newNavBgAlpha = offset / _navContentHeight; + newNavBgAlpha = offset / _navContentHeight; } - if (_navBgAlpha != _newNavBgAlpha) { + if (_navBgAlpha != newNavBgAlpha) { setState(() { - _navBgAlpha = _newNavBgAlpha; + _navBgAlpha = newNavBgAlpha; _isShowNavTitle = _navBgAlpha > .5; }); } diff --git a/example/lib/features/listview/listview_fixed_height_demo/listview_fixed_height_demo_page.dart b/example/lib/features/listview/listview_fixed_height_demo/listview_fixed_height_demo_page.dart index 6ed7b74..35f7340 100644 --- a/example/lib/features/listview/listview_fixed_height_demo/listview_fixed_height_demo_page.dart +++ b/example/lib/features/listview/listview_fixed_height_demo/listview_fixed_height_demo_page.dart @@ -3,13 +3,13 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; class ListViewFixedHeightDemoPage extends StatefulWidget { - const ListViewFixedHeightDemoPage({Key? key}) : super(key: key); + const ListViewFixedHeightDemoPage({super.key}); @override State createState() => @@ -20,8 +20,9 @@ class _ListViewFixedHeightDemoPageState extends State { int _hitIndex = 0; - ScrollController scrollController = - ScrollController(initialScrollOffset: 1000); + ScrollController scrollController = ScrollController( + initialScrollOffset: 1000, + ); late ListObserverController observerController; @@ -36,14 +37,12 @@ class _ListViewFixedHeightDemoPageState ); // Trigger an observation manually - ambiguate(WidgetsBinding.instance)?.endOfFrame.then( - (_) { - // After layout - if (mounted) { - observerController.dispatchOnceObserve(); - } - }, - ); + ambiguate(WidgetsBinding.instance)?.endOfFrame.then((_) { + // After layout + if (mounted) { + observerController.dispatchOnceObserve(); + } + }); } @override @@ -57,7 +56,6 @@ class _ListViewFixedHeightDemoPageState return Scaffold( appBar: AppBar(title: const Text("ListView")), body: ListViewObserver( - child: _buildListView(), controller: observerController, onObserve: (resultModel) { // print('visible -- ${resultModel.visible}'); @@ -74,20 +72,19 @@ class _ListViewFixedHeightDemoPageState for (var item in resultModel.displayingChildModelList) { debugPrint( - 'item - ${item.index} - ${item.leadingMarginToViewport} - ${item.trailingMarginToViewport}'); + 'item - ${item.index} - ${item.leadingMarginToViewport} - ${item.trailingMarginToViewport}', + ); } setState(() { _hitIndex = resultModel.firstChild?.index ?? 0; }); }, + child: _buildListView(), ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.airline_stops_outlined), onPressed: () { - SnackBarUtil.showSnackBar( - context: context, - text: 'Jump to row 100', - ); + SnackBarUtil.showSnackBar(context: context, text: 'Jump to row 100'); observerController.jumpTo(index: 100, isFixedHeight: true); // observerController.animateTo( // index: 100, @@ -130,9 +127,6 @@ class _ListViewFixedHeightDemoPageState } Container _buildSeparatorView() { - return Container( - color: Colors.white, - height: 5, - ); + return Container(color: Colors.white, height: 5); } } diff --git a/example/lib/features/listview/sliver_list_demo/sliver_list_demo_page.dart b/example/lib/features/listview/sliver_list_demo/sliver_list_demo_page.dart index 418b023..25c3287 100644 --- a/example/lib/features/listview/sliver_list_demo/sliver_list_demo_page.dart +++ b/example/lib/features/listview/sliver_list_demo/sliver_list_demo_page.dart @@ -3,12 +3,12 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/typedefs.dart'; class SliverListViewDemoPage extends StatefulWidget { - const SliverListViewDemoPage({Key? key}) : super(key: key); + const SliverListViewDemoPage({super.key}); @override State createState() => _SliverListViewDemoPageState(); @@ -39,16 +39,10 @@ class _SliverListViewDemoPageState extends State { appBar: AppBar(title: const Text("SliverListView")), body: ListViewObserver( child: CustomScrollView( - slivers: [ - _buildSliverListView1(), - _buildSliverListView2(), - ], + slivers: [_buildSliverListView1(), _buildSliverListView2()], ), sliverListContexts: () { - return [ - if (_sliverListViewCtx1 != null) _sliverListViewCtx1!, - if (_sliverListViewCtx2 != null) _sliverListViewCtx2!, - ]; + return [?_sliverListViewCtx1, ?_sliverListViewCtx2]; }, onObserveAll: (resultMap) { final model1 = resultMap[_sliverListViewCtx1]; @@ -77,51 +71,43 @@ class _SliverListViewDemoPageState extends State { SliverList _buildSliverListView1() { return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (_sliverListViewCtx1 != ctx) { - _sliverListViewCtx1 = ctx; - } - return Container( - height: (index % 2 == 0) ? 80 : 50, - color: _hitIndexForCtx1 == index ? Colors.red : Colors.black12, - child: Center( - child: Text( - "index -- $index", - style: TextStyle( - color: - _hitIndexForCtx1 == index ? Colors.white : Colors.black, - ), + delegate: SliverChildBuilderDelegate((ctx, index) { + if (_sliverListViewCtx1 != ctx) { + _sliverListViewCtx1 = ctx; + } + return Container( + height: (index % 2 == 0) ? 80 : 50, + color: _hitIndexForCtx1 == index ? Colors.red : Colors.black12, + child: Center( + child: Text( + "index -- $index", + style: TextStyle( + color: _hitIndexForCtx1 == index ? Colors.white : Colors.black, ), ), - ); - }, - childCount: 30, - ), + ), + ); + }, childCount: 30), ); } SliverList _buildSliverListView2() { return SliverList( key: _sliverListView2Key, - delegate: SliverChildBuilderDelegate( - (ctx, index) { - return Container( - height: (index % 2 == 0) ? 80 : 50, - color: _hitIndexForCtx2 == index ? Colors.amber : Colors.blue[50], - child: Center( - child: Text( - "index -- $index", - style: TextStyle( - color: - _hitIndexForCtx2 == index ? Colors.white : Colors.black, - ), + delegate: SliverChildBuilderDelegate((ctx, index) { + return Container( + height: (index % 2 == 0) ? 80 : 50, + color: _hitIndexForCtx2 == index ? Colors.amber : Colors.blue[50], + child: Center( + child: Text( + "index -- $index", + style: TextStyle( + color: _hitIndexForCtx2 == index ? Colors.white : Colors.black, ), ), - ); - }, - childCount: 30, - ), + ), + ); + }, childCount: 30), ); } } diff --git a/example/lib/features/nested_scrollview/nested_scrollview_demo/nested_scrollview_demo_page.dart b/example/lib/features/nested_scrollview/nested_scrollview_demo/nested_scrollview_demo_page.dart index 0560792..9ffaded 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_demo/nested_scrollview_demo_page.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_demo/nested_scrollview_demo_page.dart @@ -4,13 +4,13 @@ * @Date: 2023-11-27 22:05:28 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; import 'package:scrollview_observer_example/widgets/sliver.dart'; class NestedScrollViewDemoPage extends StatefulWidget { - const NestedScrollViewDemoPage({Key? key}) : super(key: key); + const NestedScrollViewDemoPage({super.key}); @override State createState() => @@ -40,10 +40,7 @@ class _NestedScrollViewDemoPageState extends State controller: outerScrollController, ); - late TabController tabBarController = TabController( - length: 3, - vsync: this, - ); + late TabController tabBarController = TabController(length: 3, vsync: this); bool scrollToWithAnimation = false; @@ -68,9 +65,9 @@ class _NestedScrollViewDemoPageState extends State child: _buildNestedScrollView(), sliverContexts: () { return [ - if (_sliverHeaderListCtx != null) _sliverHeaderListCtx!, - if (_sliverBodyListCtx != null) _sliverBodyListCtx!, - if (_sliverBodyGridCtx != null) _sliverBodyGridCtx!, + ?_sliverHeaderListCtx, + ?_sliverBodyListCtx, + ?_sliverBodyGridCtx, ]; }, customOverlap: (sliverContext) { @@ -83,7 +80,8 @@ class _NestedScrollViewDemoPageState extends State result.forEach((key, value) { if (key == _sliverHeaderListCtx) { debugPrint( - "SliverListHeaderCtx: ${value.displayingChildIndexList}"); + "SliverListHeaderCtx: ${value.displayingChildIndexList}", + ); } else if (key == _sliverBodyListCtx) { final model = value as ListViewObserveModel; debugPrint("SliverListCtx: ${model.displayingChildIndexList}"); @@ -94,8 +92,9 @@ class _NestedScrollViewDemoPageState extends State } else if (key == _sliverBodyGridCtx) { final model = value as GridViewObserveModel; debugPrint("SliverGridCtx: ${model.displayingChildIndexList}"); - final firstGroupChildIndexList = - model.firstGroupChildList.map((e) => e.index).toList(); + final firstGroupChildIndexList = model.firstGroupChildList + .map((e) => e.index) + .toList(); if (_hitIndexesForGrid != firstGroupChildIndexList) { _hitIndexesForGrid = firstGroupChildIndexList; setState(() {}); @@ -152,7 +151,7 @@ class _NestedScrollViewDemoPageState extends State IconButton( icon: const Icon(Icons.restore_outlined), onPressed: resetAllSliverObservationData, - ) + ), ], ), ); @@ -184,18 +183,13 @@ class _NestedScrollViewDemoPageState extends State ], ), SliverFixedExtentList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (_sliverHeaderListCtx != ctx) { - _sliverHeaderListCtx = ctx; - nestedScrollUtil.headerSliverContexts.add(ctx); - } - return ListTile( - leading: Text("Item $index"), - ); - }, - childCount: 5, - ), + delegate: SliverChildBuilderDelegate((ctx, index) { + if (_sliverHeaderListCtx != ctx) { + _sliverHeaderListCtx = ctx; + nestedScrollUtil.headerSliverContexts.add(ctx); + } + return ListTile(leading: Text("Item $index")); + }, childCount: 5), itemExtent: 50, ), SliverPersistentHeader( @@ -218,47 +212,43 @@ class _NestedScrollViewDemoPageState extends State ), ]; }, - body: Builder(builder: (context) { - // Get the inner scroll controller. - final innerScrollController = PrimaryScrollController.of(context); - if (nestedScrollUtil.bodyScrollController != innerScrollController) { - nestedScrollUtil.bodyScrollController = innerScrollController; - } - return CustomScrollView( - slivers: [ - _buildSliverListView(), - _buildSliverGridView(), - ], - ); - }), + body: Builder( + builder: (context) { + // Get the inner scroll controller. + final innerScrollController = PrimaryScrollController.of(context); + if (nestedScrollUtil.bodyScrollController != innerScrollController) { + nestedScrollUtil.bodyScrollController = innerScrollController; + } + return CustomScrollView( + slivers: [_buildSliverListView(), _buildSliverGridView()], + ); + }, + ), ); } Widget _buildSliverListView() { return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (_sliverBodyListCtx != ctx) { - _sliverBodyListCtx = ctx; - nestedScrollUtil.bodySliverContexts.add(ctx); - } - return Container( - height: (index % 2 == 0) ? 80 : 50, - color: _hitIndexForListCtx == index ? Colors.red : Colors.black12, - child: Center( - child: Text( - "index -- $index", - style: TextStyle( - color: _hitIndexForListCtx == index - ? Colors.white - : Colors.black, - ), + delegate: SliverChildBuilderDelegate((ctx, index) { + if (_sliverBodyListCtx != ctx) { + _sliverBodyListCtx = ctx; + nestedScrollUtil.bodySliverContexts.add(ctx); + } + return Container( + height: (index % 2 == 0) ? 80 : 50, + color: _hitIndexForListCtx == index ? Colors.red : Colors.black12, + child: Center( + child: Text( + "index -- $index", + style: TextStyle( + color: _hitIndexForListCtx == index + ? Colors.white + : Colors.black, ), ), - ); - }, - childCount: 30, - ), + ), + ); + }, childCount: 30), ); } @@ -270,30 +260,22 @@ class _NestedScrollViewDemoPageState extends State crossAxisSpacing: 10.0, childAspectRatio: 2.0, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - if (_sliverBodyGridCtx != context) { - _sliverBodyGridCtx = context; - nestedScrollUtil.bodySliverContexts.add(context); - } - return Container( - color: (_hitIndexesForGrid.contains(index)) - ? Colors.green - : Colors.blue[100], - child: Center( - child: Text('index -- $index'), - ), - ); - }, - childCount: 150, - ), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + if (_sliverBodyGridCtx != context) { + _sliverBodyGridCtx = context; + nestedScrollUtil.bodySliverContexts.add(context); + } + return Container( + color: (_hitIndexesForGrid.contains(index)) + ? Colors.green + : Colors.blue[100], + child: Center(child: Text('index -- $index')), + ); + }, childCount: 150), ); } - double calcPersistentHeaderExtent( - double offset, { - required bool isBody, - }) { + double calcPersistentHeaderExtent(double offset, {required bool isBody}) { double value = ObserverUtils.calcPersistentHeaderExtent( key: appBarKey, offset: offset, @@ -307,7 +289,7 @@ class _NestedScrollViewDemoPageState extends State return value; } - resetAllSliverObservationData() { + void resetAllSliverObservationData() { nestedScrollUtil.reset(); nestedScrollViewKey = GlobalKey(); nestedScrollUtil.outerScrollController = outerScrollController; @@ -321,7 +303,7 @@ class _NestedScrollViewDemoPageState extends State }); } - scrollTo({ + void scrollTo({ required NestedScrollUtilPosition position, required int index, required BuildContext? sliverContext, @@ -337,10 +319,7 @@ class _NestedScrollViewDemoPageState extends State curve: Curves.easeInOut, sliverContext: sliverContext, offset: (targetOffset) { - return calcPersistentHeaderExtent( - targetOffset, - isBody: isBody, - ); + return calcPersistentHeaderExtent(targetOffset, isBody: isBody); }, ); } else { @@ -351,10 +330,7 @@ class _NestedScrollViewDemoPageState extends State index: index, sliverContext: sliverContext, offset: (targetOffset) { - return calcPersistentHeaderExtent( - targetOffset, - isBody: isBody, - ); + return calcPersistentHeaderExtent(targetOffset, isBody: isBody); }, ); } diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart index 77b7c49..bc9e1be 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart @@ -4,16 +4,16 @@ * @Date: 2026-02-23 22:19:39 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:getx_helper/getx_helper.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic.dart'; -typedef NestedScrollviewTabBarViewDemoLogicPutMixin - = GetxLogicPutStateMixin; +typedef NestedScrollviewTabBarViewDemoLogicPutMixin = + GetxLogicPutStateMixin; typedef NestedScrollviewTabBarViewDemoLogicConsumerMixin< - W extends StatefulWidget> - = GetxLogicConsumerStateMixin; + W extends StatefulWidget +> = GetxLogicConsumerStateMixin; enum NestedScrollviewTabBarViewDemoUpdateType { scrollTypeSwitch, @@ -30,9 +30,7 @@ enum NestedScrollviewTabBarViewDemoTabType { tab2(title: "Grid"), tab3(title: "List+Grid"); - const NestedScrollviewTabBarViewDemoTabType({ - required this.title, - }); + const NestedScrollviewTabBarViewDemoTabType({required this.title}); final String title; } diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_floating_action_btn.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_floating_action_btn.dart index 71e9e8a..6332613 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_floating_action_btn.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_floating_action_btn.dart @@ -12,9 +12,7 @@ import 'package:scrollview_observer_example/utils/snackbar.dart'; extension NestedScrollViewTabBarViewDemoLogicForFAB on NestedScrollViewTabBarViewDemoLogic { - void handleFABClick( - NestedScrollviewTabBarViewDemoFABClickType type, - ) { + void handleFABClick(NestedScrollviewTabBarViewDemoFABClickType type) { final context = state.rootContext; if (context == null) return; diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_observer.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_observer.dart index 08c9c76..c16bb1b 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_observer.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_observer.dart @@ -4,7 +4,7 @@ * @Date: 2026-02-24 23:16:49 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic.dart'; @@ -17,9 +17,7 @@ extension NestedScrollViewTabBarViewDemoLogicForObserver required Function() toRecordCtx, }) { if (oldCtx == newCtx) return; - state.nestedScrollUtil.headerSliverContexts.remove( - oldCtx, - ); + state.nestedScrollUtil.headerSliverContexts.remove(oldCtx); toRecordCtx.call(); state.nestedScrollUtil.headerSliverContexts.add(newCtx); } @@ -30,9 +28,7 @@ extension NestedScrollViewTabBarViewDemoLogicForObserver required Function() toRecordCtx, }) { if (oldCtx == newCtx) return; - state.nestedScrollUtil.bodySliverContexts.remove( - oldCtx, - ); + state.nestedScrollUtil.bodySliverContexts.remove(oldCtx); toRecordCtx.call(); state.nestedScrollUtil.bodySliverContexts.add(newCtx); } @@ -45,9 +41,7 @@ extension NestedScrollViewTabBarViewDemoLogicForObserver debugPrint("SliverListHeaderCtx: ${model.displayingChildIndexList}"); if (state.hitIndexForHeaderListCtx == model.firstChild?.index) return; state.hitIndexForHeaderListCtx = model.firstChild?.index ?? 0; - update([ - NestedScrollviewTabBarViewDemoUpdateType.headerSliverList, - ]); + update([NestedScrollviewTabBarViewDemoUpdateType.headerSliverList]); return; } @@ -57,9 +51,7 @@ extension NestedScrollViewTabBarViewDemoLogicForObserver debugPrint("sliverTab1ListCtx: ${model.displayingChildIndexList}"); if (state.hitIndexForTab1ListCtx == model.firstChild?.index) return; state.hitIndexForTab1ListCtx = model.firstChild?.index ?? 0; - update([ - NestedScrollviewTabBarViewDemoUpdateType.tab1View, - ]); + update([NestedScrollviewTabBarViewDemoUpdateType.tab1View]); return; } @@ -67,13 +59,12 @@ extension NestedScrollViewTabBarViewDemoLogicForObserver if (key == state.tab2SliverGridCtx) { final model = value as GridViewObserveModel; debugPrint("sliverTab2GridCtx: ${model.displayingChildIndexList}"); - final firstGroupChildIndexList = - model.firstGroupChildList.map((e) => e.index).toList(); + final firstGroupChildIndexList = model.firstGroupChildList + .map((e) => e.index) + .toList(); if (state.hitIndexesForTab2Grid == firstGroupChildIndexList) return; state.hitIndexesForTab2Grid = firstGroupChildIndexList; - update([ - NestedScrollviewTabBarViewDemoUpdateType.tab2View, - ]); + update([NestedScrollviewTabBarViewDemoUpdateType.tab2View]); return; } @@ -83,9 +74,7 @@ extension NestedScrollViewTabBarViewDemoLogicForObserver debugPrint("sliverTab3ListCtx: ${model.displayingChildIndexList}"); if (state.hitIndexForTab3ListCtx == model.firstChild?.index) return; state.hitIndexForTab3ListCtx = model.firstChild?.index ?? 0; - update([ - NestedScrollviewTabBarViewDemoUpdateType.tab3ViewSliverList, - ]); + update([NestedScrollviewTabBarViewDemoUpdateType.tab3ViewSliverList]); return; } @@ -93,13 +82,12 @@ extension NestedScrollViewTabBarViewDemoLogicForObserver if (key == state.tab3SliverGridCtx) { final model = value as GridViewObserveModel; debugPrint("sliverTab3GridCtx: ${model.displayingChildIndexList}"); - final firstGroupChildIndexList = - model.firstGroupChildList.map((e) => e.index).toList(); + final firstGroupChildIndexList = model.firstGroupChildList + .map((e) => e.index) + .toList(); if (state.hitIndexesForTab3Grid == firstGroupChildIndexList) return; state.hitIndexesForTab3Grid = firstGroupChildIndexList; - update([ - NestedScrollviewTabBarViewDemoUpdateType.tab3ViewSliverGrid, - ]); + update([NestedScrollviewTabBarViewDemoUpdateType.tab3ViewSliverGrid]); return; } }); @@ -121,10 +109,7 @@ extension NestedScrollViewTabBarViewDemoLogicForObserver curve: Curves.easeInOut, sliverContext: sliverContext, offset: (targetOffset) { - return calcPersistentHeaderExtent( - targetOffset, - isBody: isBody, - ); + return calcPersistentHeaderExtent(targetOffset, isBody: isBody); }, ); } else { @@ -135,19 +120,13 @@ extension NestedScrollViewTabBarViewDemoLogicForObserver index: index, sliverContext: sliverContext, offset: (targetOffset) { - return calcPersistentHeaderExtent( - targetOffset, - isBody: isBody, - ); + return calcPersistentHeaderExtent(targetOffset, isBody: isBody); }, ); } } - double calcPersistentHeaderExtent( - double offset, { - required bool isBody, - }) { + double calcPersistentHeaderExtent(double offset, {required bool isBody}) { double value = ObserverUtils.calcPersistentHeaderExtent( key: state.appBarKey, offset: offset, diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_scroll_type_switch.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_scroll_type_switch.dart index 32d3d68..76e2f53 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_scroll_type_switch.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_scroll_type_switch.dart @@ -15,9 +15,7 @@ extension NestedScrollViewTabBarViewDemoLogicForScrollTypeSwitch if (context == null) return; state.scrollToWithAnimation = value; - update([ - NestedScrollviewTabBarViewDemoUpdateType.scrollTypeSwitch, - ]); + update([NestedScrollviewTabBarViewDemoUpdateType.scrollTypeSwitch]); SnackBarUtil.showSnackBar( context: context, text: diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_tab_bar.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_tab_bar.dart index 98ef338..b38e728 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_tab_bar.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic_tab_bar.dart @@ -4,7 +4,7 @@ * @Date: 2026-02-24 23:14:47 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic.dart'; @@ -17,9 +17,7 @@ extension NestedScrollViewTabBarViewDemoLogicForTabBar ); state.tabController.addListener(() { if (state.tabController.indexIsChanging) return; - update([ - NestedScrollviewTabBarViewDemoUpdateType.floatingActionButton, - ]); + update([NestedScrollviewTabBarViewDemoUpdateType.floatingActionButton]); }); } diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/page/nested_scrollview_tab_bar_view_demo_page.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/page/nested_scrollview_tab_bar_view_demo_page.dart index cbbd6d9..11100ec 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/page/nested_scrollview_tab_bar_view_demo_page.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/page/nested_scrollview_tab_bar_view_demo_page.dart @@ -4,7 +4,7 @@ * @Date: 2026-02-23 22:19:39 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart'; @@ -32,7 +32,8 @@ class NestedScrollViewTabBarViewDemoPageState extends State with NestedScrollviewTabBarViewDemoLogicPutMixin< - NestedScrollViewTabBarViewDemoPage>, + NestedScrollViewTabBarViewDemoPage + >, TickerProviderStateMixin { NestedScrollViewTabBarViewDemoState get state => logic.state; @@ -85,17 +86,18 @@ class NestedScrollViewTabBarViewDemoPageState ), ]; }, - body: Builder(builder: (context) { - final innerScrollController = PrimaryScrollController.of(context); - if (nestedScrollUtil.bodyScrollController != innerScrollController) { - nestedScrollUtil.bodyScrollController = innerScrollController; - } - return _buildTabBarView(); - }), + body: Builder( + builder: (context) { + final innerScrollController = PrimaryScrollController.of(context); + if (nestedScrollUtil.bodyScrollController != innerScrollController) { + nestedScrollUtil.bodyScrollController = innerScrollController; + } + return _buildTabBarView(); + }, + ), ); resultWidget = SliverViewObserver( - child: resultWidget, sliverContexts: () { final sliverHeaderListCtx = state.headerSliverListCtx; final sliverTab1ListCtx = state.sliverTab1ListCtx; @@ -104,11 +106,11 @@ class NestedScrollViewTabBarViewDemoPageState final sliverTab3GridCtx = state.tab3SliverGridCtx; return [ - if (sliverHeaderListCtx != null) sliverHeaderListCtx, - if (sliverTab1ListCtx != null) sliverTab1ListCtx, - if (sliverTab2GridCtx != null) sliverTab2GridCtx, - if (sliverTab3ListCtx != null) sliverTab3ListCtx, - if (sliverTab3GridCtx != null) sliverTab3GridCtx, + ?sliverHeaderListCtx, + ?sliverTab1ListCtx, + ?sliverTab2GridCtx, + ?sliverTab3ListCtx, + ?sliverTab3GridCtx, ]; }, customOverlap: (sliverContext) { @@ -118,6 +120,7 @@ class NestedScrollViewTabBarViewDemoPageState ); }, onObserveAll: logic.handleOnObserveAll, + child: resultWidget, ); return resultWidget; @@ -140,9 +143,7 @@ class NestedScrollViewTabBarViewDemoPageState title: const Text("Nested & TabBarView"), pinned: true, forceElevated: innerBoxIsScrolled, - actions: const [ - NestedScrollviewTabBarViewDemoScrollTypeSwitch(), - ], + actions: const [NestedScrollviewTabBarViewDemoScrollTypeSwitch()], ); } } diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/state/nested_scrollview_tab_bar_view_demo_state.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/state/nested_scrollview_tab_bar_view_demo_state.dart index b20c8a1..0e73561 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/state/nested_scrollview_tab_bar_view_demo_state.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/state/nested_scrollview_tab_bar_view_demo_state.dart @@ -4,7 +4,7 @@ * @Date: 2026-02-23 22:19:39 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart'; diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_floating_action_btn.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_floating_action_btn.dart index bf3b905..560fe26 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_floating_action_btn.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_floating_action_btn.dart @@ -4,7 +4,7 @@ * @Date: 2026-02-24 23:12:26 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic.dart'; @@ -23,7 +23,8 @@ class _NestedScrollviewTabBarViewDemoFloatingActionBtnState extends State with NestedScrollviewTabBarViewDemoLogicConsumerMixin< - NestedScrollviewTabBarViewDemoFloatingActionBtn> { + NestedScrollviewTabBarViewDemoFloatingActionBtn + > { NestedScrollViewTabBarViewDemoState get state => logic.state; @override diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_header_list_sliver.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_header_list_sliver.dart index cb5b842..49553ad 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_header_list_sliver.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_header_list_sliver.dart @@ -4,7 +4,7 @@ * @Date: 2026-02-24 23:03:32 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic.dart'; @@ -23,7 +23,8 @@ class _NestedScrollviewTabBarViewDemoHeaderListSliverState extends State with NestedScrollviewTabBarViewDemoLogicConsumerMixin< - NestedScrollviewTabBarViewDemoHeaderListSliver> { + NestedScrollviewTabBarViewDemoHeaderListSliver + > { NestedScrollViewTabBarViewDemoState get state => logic.state; @override @@ -39,27 +40,24 @@ class _NestedScrollviewTabBarViewDemoHeaderListSliverState Widget _buildSliverList() { return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - logic.updateNestedScrollUtilHeaderSliverContextsIfNeed( - oldCtx: state.headerSliverListCtx, - newCtx: ctx, - toRecordCtx: () { - state.headerSliverListCtx = ctx; - }, - ); + delegate: SliverChildBuilderDelegate((ctx, index) { + logic.updateNestedScrollUtilHeaderSliverContextsIfNeed( + oldCtx: state.headerSliverListCtx, + newCtx: ctx, + toRecordCtx: () { + state.headerSliverListCtx = ctx; + }, + ); - return ListTile( - title: Text("Header Item $index"), - tileColor: state.hitIndexForHeaderListCtx == index - ? Colors.orange - : index % 2 == 0 - ? Colors.grey[100] - : Colors.white, - ); - }, - childCount: 5, - ), + return ListTile( + title: Text("Header Item $index"), + tileColor: state.hitIndexForHeaderListCtx == index + ? Colors.orange + : index % 2 == 0 + ? Colors.grey[100] + : Colors.white, + ); + }, childCount: 5), ); } } diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_scroll_type_switch.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_scroll_type_switch.dart index 2950978..c741f76 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_scroll_type_switch.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_scroll_type_switch.dart @@ -4,7 +4,7 @@ * @Date: 2026-02-24 23:54:30 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic.dart'; @@ -23,7 +23,8 @@ class _NestedScrollviewTabBarViewDemoScrollTypeSwitchState extends State with NestedScrollviewTabBarViewDemoLogicConsumerMixin< - NestedScrollviewTabBarViewDemoScrollTypeSwitch> { + NestedScrollviewTabBarViewDemoScrollTypeSwitch + > { NestedScrollViewTabBarViewDemoState get state => logic.state; @override diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab1_view.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab1_view.dart index 19fe174..f5a4f7a 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab1_view.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab1_view.dart @@ -4,7 +4,7 @@ * @Date: 2026-02-24 22:25:39 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic.dart'; @@ -23,7 +23,8 @@ class _NestedScrollviewTabBarViewDemoTab1ViewState extends State with NestedScrollviewTabBarViewDemoLogicConsumerMixin< - NestedScrollviewTabBarViewDemoTab1View> { + NestedScrollviewTabBarViewDemoTab1View + > { NestedScrollViewTabBarViewDemoState get state => logic.state; @override @@ -40,32 +41,26 @@ class _NestedScrollviewTabBarViewDemoTab1ViewState Widget _buildBody() { return CustomScrollView( key: const PageStorageKey("tab1"), - slivers: [ - _buildSliverList(), - ], + slivers: [_buildSliverList()], ); } Widget _buildSliverList() { return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - logic.updateNestedScrollUtilBodySliverContextsIfNeed( - oldCtx: state.sliverTab1ListCtx, - newCtx: ctx, - toRecordCtx: () { - state.sliverTab1ListCtx = ctx; - }, - ); + delegate: SliverChildBuilderDelegate((ctx, index) { + logic.updateNestedScrollUtilBodySliverContextsIfNeed( + oldCtx: state.sliverTab1ListCtx, + newCtx: ctx, + toRecordCtx: () { + state.sliverTab1ListCtx = ctx; + }, + ); - return ListTile( - tileColor: - state.hitIndexForTab1ListCtx == index ? Colors.red : null, - title: Text("Tab 1 - List Item $index"), - ); - }, - childCount: 30, - ), + return ListTile( + tileColor: state.hitIndexForTab1ListCtx == index ? Colors.red : null, + title: Text("Tab 1 - List Item $index"), + ); + }, childCount: 30), ); } } diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab2_view.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab2_view.dart index 8af8f1f..c56e8c8 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab2_view.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab2_view.dart @@ -4,7 +4,7 @@ * @Date: 2026-02-24 22:33:19 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic.dart'; @@ -23,7 +23,8 @@ class _NestedScrollviewTabBarViewDemoTab2ViewState extends State with NestedScrollviewTabBarViewDemoLogicConsumerMixin< - NestedScrollviewTabBarViewDemoTab2View> { + NestedScrollviewTabBarViewDemoTab2View + > { NestedScrollViewTabBarViewDemoState get state => logic.state; @override @@ -40,9 +41,7 @@ class _NestedScrollviewTabBarViewDemoTab2ViewState Widget _buildBody() { return CustomScrollView( key: const PageStorageKey("tab2"), - slivers: [ - _buildSliverGrid(), - ], + slivers: [_buildSliverGrid()], ); } @@ -54,26 +53,23 @@ class _NestedScrollviewTabBarViewDemoTab2ViewState crossAxisSpacing: 10.0, childAspectRatio: 2.0, ), - delegate: SliverChildBuilderDelegate( - (ctx, index) { - logic.updateNestedScrollUtilBodySliverContextsIfNeed( - oldCtx: state.tab2SliverGridCtx, - newCtx: ctx, - toRecordCtx: () { - state.tab2SliverGridCtx = ctx; - }, - ); + delegate: SliverChildBuilderDelegate((ctx, index) { + logic.updateNestedScrollUtilBodySliverContextsIfNeed( + oldCtx: state.tab2SliverGridCtx, + newCtx: ctx, + toRecordCtx: () { + state.tab2SliverGridCtx = ctx; + }, + ); - return Container( - color: state.hitIndexesForTab2Grid.contains(index) - ? Colors.green - : Colors.blue[100], - alignment: Alignment.center, - child: Text('Tab 2 - Grid Item $index'), - ); - }, - childCount: 30, - ), + return Container( + color: state.hitIndexesForTab2Grid.contains(index) + ? Colors.green + : Colors.blue[100], + alignment: Alignment.center, + child: Text('Tab 2 - Grid Item $index'), + ); + }, childCount: 30), ); resultWidget = SliverPadding( padding: const EdgeInsets.all(8), diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab3_view.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab3_view.dart index 10d3cbf..b464ac4 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab3_view.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tab3_view.dart @@ -4,7 +4,7 @@ * @Date: 2026-02-24 22:35:11 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/logic/nested_scrollview_tab_bar_view_demo_logic.dart'; @@ -23,7 +23,8 @@ class _NestedScrollviewTabBarViewDemoTab3ViewState extends State with NestedScrollviewTabBarViewDemoLogicConsumerMixin< - NestedScrollviewTabBarViewDemoTab3View> { + NestedScrollviewTabBarViewDemoTab3View + > { NestedScrollViewTabBarViewDemoState get state => logic.state; @override @@ -47,24 +48,20 @@ class _NestedScrollviewTabBarViewDemoTab3ViewState Widget _buildSliverList() { return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - logic.updateNestedScrollUtilBodySliverContextsIfNeed( - oldCtx: state.tab3SliverListCtx, - newCtx: ctx, - toRecordCtx: () { - state.tab3SliverListCtx = ctx; - }, - ); + delegate: SliverChildBuilderDelegate((ctx, index) { + logic.updateNestedScrollUtilBodySliverContextsIfNeed( + oldCtx: state.tab3SliverListCtx, + newCtx: ctx, + toRecordCtx: () { + state.tab3SliverListCtx = ctx; + }, + ); - return ListTile( - tileColor: - state.hitIndexForTab3ListCtx == index ? Colors.red : null, - title: Text("Tab 3 - List Item $index"), - ); - }, - childCount: 10, - ), + return ListTile( + tileColor: state.hitIndexForTab3ListCtx == index ? Colors.red : null, + title: Text("Tab 3 - List Item $index"), + ); + }, childCount: 10), ); } @@ -76,26 +73,23 @@ class _NestedScrollviewTabBarViewDemoTab3ViewState crossAxisSpacing: 10.0, childAspectRatio: 2.0, ), - delegate: SliverChildBuilderDelegate( - (ctx, index) { - logic.updateNestedScrollUtilBodySliverContextsIfNeed( - oldCtx: state.tab3SliverGridCtx, - newCtx: ctx, - toRecordCtx: () { - state.tab3SliverGridCtx = ctx; - }, - ); + delegate: SliverChildBuilderDelegate((ctx, index) { + logic.updateNestedScrollUtilBodySliverContextsIfNeed( + oldCtx: state.tab3SliverGridCtx, + newCtx: ctx, + toRecordCtx: () { + state.tab3SliverGridCtx = ctx; + }, + ); - return Container( - color: state.hitIndexesForTab3Grid.contains(index) - ? Colors.green - : Colors.green[100], - alignment: Alignment.center, - child: Text('Tab 3 - Grid Item $index'), - ); - }, - childCount: 20, - ), + return Container( + color: state.hitIndexesForTab3Grid.contains(index) + ? Colors.green + : Colors.green[100], + alignment: Alignment.center, + child: Text('Tab 3 - Grid Item $index'), + ); + }, childCount: 20), ); resultWidget = SliverPadding( padding: const EdgeInsets.all(8), diff --git a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tabbar.dart b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tabbar.dart index 0f20758..079cfb4 100644 --- a/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tabbar.dart +++ b/example/lib/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/widget/nested_scrollview_tab_bar_view_demo_tabbar.dart @@ -4,7 +4,7 @@ * @Date: 2026-02-24 21:28:34 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/header/nested_scrollview_tab_bar_view_demo_header.dart'; import 'package:scrollview_observer_example/features/nested_scrollview/nested_scrollview_tab_bar_view_demo/state/nested_scrollview_tab_bar_view_demo_state.dart'; @@ -20,7 +20,8 @@ class _NestedScrollViewTabBarViewDemoTabBarState extends State with NestedScrollviewTabBarViewDemoLogicConsumerMixin< - NestedScrollViewTabBarViewDemoTabBar> { + NestedScrollViewTabBarViewDemoTabBar + > { NestedScrollViewTabBarViewDemoState get state => logic.state; @override diff --git a/example/lib/features/pageview/pageview_demo/pageview_demo_page.dart b/example/lib/features/pageview/pageview_demo/pageview_demo_page.dart index 546a9ee..17bb586 100644 --- a/example/lib/features/pageview/pageview_demo/pageview_demo_page.dart +++ b/example/lib/features/pageview/pageview_demo/pageview_demo_page.dart @@ -4,12 +4,12 @@ * @Date: 2024-08-03 14:09:53 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; class PageViewDemoPage extends StatefulWidget { - const PageViewDemoPage({Key? key}) : super(key: key); + const PageViewDemoPage({super.key}); @override State createState() => _PageViewDemoPageState(); @@ -20,21 +20,24 @@ class _PageViewDemoPageState extends State { late PageController pageController; - List pageItemBgPicList = [ - '11898897', - '26653530', - '12974784', - '943459', - '4424178', - '20433037', - '4424137', - '4955810', - '4424137', - '18847956', - ] - .map((id) => - 'https://images.pexels.com/photos/$id/pexels-photo-$id.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2') - .toList(); + List pageItemBgPicList = + [ + '11898897', + '26653530', + '12974784', + '943459', + '4424178', + '20433037', + '4424137', + '4955810', + '4424137', + '18847956', + ] + .map( + (id) => + 'https://images.pexels.com/photos/$id/pexels-photo-$id.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + ) + .toList(); int get pageItemCount => pageItemBgPicList.length; @@ -45,16 +48,10 @@ class _PageViewDemoPageState extends State { @override void initState() { super.initState(); - pageController = PageController( - initialPage: 4, - viewportFraction: 0.8, - ); - pageItemOffsetYList = List.generate( - pageItemCount, - (index) { - return ValueNotifier(0); - }, - ); + pageController = PageController(initialPage: 4, viewportFraction: 0.8); + pageItemOffsetYList = List.generate(pageItemCount, (index) { + return ValueNotifier(0); + }); Future.delayed(const Duration(milliseconds: 100)).then((_) { observerController.dispatchOnceObserve(); @@ -74,18 +71,10 @@ class _PageViewDemoPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text( - "PageView", - style: TextStyle( - color: Colors.white, - ), - ), + title: const Text("PageView", style: TextStyle(color: Colors.white)), backgroundColor: Colors.black87, leading: IconButton( - icon: const Icon( - Icons.arrow_back_ios_new, - color: Colors.white, - ), + icon: const Icon(Icons.arrow_back_ios_new, color: Colors.white), onPressed: () { Navigator.of(context).pop(); }, @@ -94,12 +83,7 @@ class _PageViewDemoPageState extends State { body: Stack( children: [ _buildMap(), - Positioned( - left: 0, - right: 0, - bottom: 0, - child: _buildPageView(), - ), + Positioned(left: 0, right: 0, bottom: 0, child: _buildPageView()), ], ), ); @@ -122,9 +106,7 @@ class _PageViewDemoPageState extends State { right: 0, top: 0, bottom: 0, - child: Container( - color: Colors.black38, - ), + child: Container(color: Colors.black38), ), ], ); @@ -141,7 +123,6 @@ class _PageViewDemoPageState extends State { ); resultWidget = ListViewObserver( controller: observerController, - child: resultWidget, triggerOnObserveType: ObserverTriggerOnObserveType.directly, onObserve: (resultModel) { final displayingChildModelList = resultModel.displayingChildModelList; @@ -157,11 +138,9 @@ class _PageViewDemoPageState extends State { customTargetRenderSliverType: (renderObj) { return renderObj is RenderSliverFillViewport; }, - ); - resultWidget = SizedBox( - height: 300, child: resultWidget, ); + resultWidget = SizedBox(height: 300, child: resultWidget); return resultWidget; } @@ -187,9 +166,7 @@ class _PageViewDemoPageState extends State { width: double.infinity, alignment: Alignment.center, height: 44, - decoration: const BoxDecoration( - color: Colors.white54, - ), + decoration: const BoxDecoration(color: Colors.white54), child: Text("Page $index"), ), ], @@ -212,9 +189,6 @@ class _PageViewDemoPageState extends State { } Widget _buildPageItemBgPicView(int index) { - return Image.network( - pageItemBgPicList[index], - fit: BoxFit.cover, - ); + return Image.network(pageItemBgPicList[index], fit: BoxFit.cover); } } diff --git a/example/lib/features/pageview/pageview_demo/pageview_parallax_item_listener_page.dart b/example/lib/features/pageview/pageview_demo/pageview_parallax_item_listener_page.dart index 0b3b9d4..d0b73d8 100644 --- a/example/lib/features/pageview/pageview_demo/pageview_parallax_item_listener_page.dart +++ b/example/lib/features/pageview/pageview_demo/pageview_parallax_item_listener_page.dart @@ -4,13 +4,13 @@ * @Date: 2024-11-14 22:41:51 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; class PageViewParallaxItemListenerPage extends StatefulWidget { - const PageViewParallaxItemListenerPage({Key? key}) : super(key: key); + const PageViewParallaxItemListenerPage({super.key}); @override State createState() => @@ -21,21 +21,24 @@ class _PageViewParallaxItemListenerPageState extends State { late PageController pageController; - List pageItemBgPicList = [ - '11898897', - '26653530', - '12974784', - '943459', - '4424178', - '20433037', - '4424137', - '4955810', - '4424137', - '18847956', - ] - .map((id) => - 'https://images.pexels.com/photos/$id/pexels-photo-$id.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2') - .toList(); + List pageItemBgPicList = + [ + '11898897', + '26653530', + '12974784', + '943459', + '4424178', + '20433037', + '4424137', + '4955810', + '4424137', + '18847956', + ] + .map( + (id) => + 'https://images.pexels.com/photos/$id/pexels-photo-$id.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + ) + .toList(); int get pageItemCount => pageItemBgPicList.length; @@ -46,16 +49,10 @@ class _PageViewParallaxItemListenerPageState @override void initState() { super.initState(); - pageController = PageController( - initialPage: 4, - viewportFraction: 0.9, - ); - pageItemBgPicAlignmentXList = List.generate( - pageItemCount, - (index) { - return ValueNotifier(0); - }, - ); + pageController = PageController(initialPage: 4, viewportFraction: 0.9); + pageItemBgPicAlignmentXList = List.generate(pageItemCount, (index) { + return ValueNotifier(0); + }); Future.delayed(const Duration(milliseconds: 100)).then((_) { observerController.dispatchOnceObserve(); @@ -78,24 +75,17 @@ class _PageViewParallaxItemListenerPageState appBar: AppBar( title: const Text( "PageView - Parallax", - style: TextStyle( - color: Colors.white, - ), + style: TextStyle(color: Colors.white), ), backgroundColor: Colors.black87, leading: IconButton( - icon: const Icon( - Icons.arrow_back_ios_new, - color: Colors.white, - ), + icon: const Icon(Icons.arrow_back_ios_new, color: Colors.white), onPressed: () { Navigator.of(context).pop(); }, ), ), - body: Center( - child: _buildPageView(), - ), + body: Center(child: _buildPageView()), ); } @@ -104,24 +94,22 @@ class _PageViewParallaxItemListenerPageState controller: pageController, itemBuilder: (context, index) { // return _buildPageItem(index); - return ParallaxItemView( - index: index, - imgUrl: pageItemBgPicList[index], - ); + return ParallaxItemView(index: index, imgUrl: pageItemBgPicList[index]); }, itemCount: pageItemCount, ); resultWidget = ListViewObserver( controller: observerController, - child: resultWidget, triggerOnObserveType: ObserverTriggerOnObserveType.directly, customTargetRenderSliverType: (renderObj) { return renderObj is RenderSliverFillViewport; }, + child: resultWidget, ); resultWidget = SizedBox( - height: (MediaQuery.sizeOf(context).height - + height: + (MediaQuery.sizeOf(context).height - MediaQuery.paddingOf(context).top - kToolbarHeight) * 0.8, @@ -136,10 +124,10 @@ class ParallaxItemView extends StatefulWidget { final String imgUrl; const ParallaxItemView({ - Key? key, + super.key, required this.index, required this.imgUrl, - }) : super(key: key); + }); @override State createState() => _ParallaxItemViewState(); @@ -156,9 +144,7 @@ class _ParallaxItemViewState extends State { removeListener(); observerState = ListViewObserver.of(context) - ..addListener( - onObserve: handleObserverResult, - ); + ..addListener(onObserve: handleObserverResult); } @override @@ -169,15 +155,11 @@ class _ParallaxItemViewState extends State { } void removeListener() { - observerState?.removeListener( - onObserve: handleObserverResult, - ); + observerState?.removeListener(onObserve: handleObserverResult); observerState = null; } - void handleObserverResult( - ListViewObserveModel result, - ) { + void handleObserverResult(ListViewObserveModel result) { if (result.displayingChildModelMap.isEmpty) return; final model = result.displayingChildModelMap[widget.index]; if (model == null) { diff --git a/example/lib/features/pageview/pageview_demo/pageview_parallax_page.dart b/example/lib/features/pageview/pageview_demo/pageview_parallax_page.dart index c2d22d1..248abea 100644 --- a/example/lib/features/pageview/pageview_demo/pageview_parallax_page.dart +++ b/example/lib/features/pageview/pageview_demo/pageview_parallax_page.dart @@ -4,12 +4,12 @@ * @Date: 2024-08-26 21:30:59 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; class PageViewParallaxPage extends StatefulWidget { - const PageViewParallaxPage({Key? key}) : super(key: key); + const PageViewParallaxPage({super.key}); @override State createState() => _PageViewParallaxPageState(); @@ -18,21 +18,24 @@ class PageViewParallaxPage extends StatefulWidget { class _PageViewParallaxPageState extends State { late PageController pageController; - List pageItemBgPicList = [ - '11898897', - '26653530', - '12974784', - '943459', - '4424178', - '20433037', - '4424137', - '4955810', - '4424137', - '18847956', - ] - .map((id) => - 'https://images.pexels.com/photos/$id/pexels-photo-$id.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2') - .toList(); + List pageItemBgPicList = + [ + '11898897', + '26653530', + '12974784', + '943459', + '4424178', + '20433037', + '4424137', + '4955810', + '4424137', + '18847956', + ] + .map( + (id) => + 'https://images.pexels.com/photos/$id/pexels-photo-$id.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + ) + .toList(); int get pageItemCount => pageItemBgPicList.length; @@ -43,16 +46,10 @@ class _PageViewParallaxPageState extends State { @override void initState() { super.initState(); - pageController = PageController( - initialPage: 4, - viewportFraction: 0.9, - ); - pageItemBgPicAlignmentXList = List.generate( - pageItemCount, - (index) { - return ValueNotifier(0); - }, - ); + pageController = PageController(initialPage: 4, viewportFraction: 0.9); + pageItemBgPicAlignmentXList = List.generate(pageItemCount, (index) { + return ValueNotifier(0); + }); Future.delayed(const Duration(milliseconds: 100)).then((_) { observerController.dispatchOnceObserve(); @@ -75,24 +72,17 @@ class _PageViewParallaxPageState extends State { appBar: AppBar( title: const Text( "PageView - Parallax", - style: TextStyle( - color: Colors.white, - ), + style: TextStyle(color: Colors.white), ), backgroundColor: Colors.black87, leading: IconButton( - icon: const Icon( - Icons.arrow_back_ios_new, - color: Colors.white, - ), + icon: const Icon(Icons.arrow_back_ios_new, color: Colors.white), onPressed: () { Navigator.of(context).pop(); }, ), ), - body: Center( - child: _buildPageView(), - ), + body: Center(child: _buildPageView()), ); } @@ -106,7 +96,6 @@ class _PageViewParallaxPageState extends State { ); resultWidget = ListViewObserver( controller: observerController, - child: resultWidget, triggerOnObserveType: ObserverTriggerOnObserveType.directly, onObserve: (resultModel) { final displayingChildModelList = resultModel.displayingChildModelList; @@ -130,10 +119,12 @@ class _PageViewParallaxPageState extends State { customTargetRenderSliverType: (renderObj) { return renderObj is RenderSliverFillViewport; }, + child: resultWidget, ); resultWidget = SizedBox( - height: (MediaQuery.sizeOf(context).height - + height: + (MediaQuery.sizeOf(context).height - MediaQuery.paddingOf(context).top - kToolbarHeight) * 0.8, diff --git a/example/lib/features/scene/anchor_demo/anchor_page.dart b/example/lib/features/scene/anchor_demo/anchor_page.dart index f7fcd6c..86f30f0 100644 --- a/example/lib/features/scene/anchor_demo/anchor_page.dart +++ b/example/lib/features/scene/anchor_demo/anchor_page.dart @@ -3,11 +3,11 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; class AnchorListPage extends StatefulWidget { - const AnchorListPage({Key? key}) : super(key: key); + const AnchorListPage({super.key}); @override State createState() => _AnchorListPageState(); @@ -90,18 +90,13 @@ class _AnchorListPageState extends State child: Center( child: Text( "index -- $index", - style: const TextStyle( - color: Colors.black, - ), + style: const TextStyle(color: Colors.black), ), ), ); } Container _buildSeparatorView() { - return Container( - color: Colors.white, - height: 5, - ); + return Container(color: Colors.white, height: 5); } } diff --git a/example/lib/features/scene/anchor_demo/anchor_waterfall_page.dart b/example/lib/features/scene/anchor_demo/anchor_waterfall_page.dart index 91e3f50..a8a0e21 100644 --- a/example/lib/features/scene/anchor_demo/anchor_waterfall_page.dart +++ b/example/lib/features/scene/anchor_demo/anchor_waterfall_page.dart @@ -4,12 +4,12 @@ * @Date: 2023-05-27 11:51:24 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:waterfall_flow/waterfall_flow.dart'; class AnchorWaterfallPage extends StatefulWidget { - const AnchorWaterfallPage({Key? key}) : super(key: key); + const AnchorWaterfallPage({super.key}); @override State createState() => _AnchorWaterfallPageState(); @@ -59,16 +59,17 @@ class _AnchorWaterfallPageState extends State ), ), body: GridViewObserver( - child: _buildGridView(), controller: observerController, customTargetRenderSliverType: (renderObject) { return renderObject is RenderSliverWaterfallFlow; }, onObserve: (resultModel) { debugPrint( - 'firstGroupChildIndexList -- ${resultModel.firstGroupChildList.map((e) => e.index).toList()}'); + 'firstGroupChildIndexList -- ${resultModel.firstGroupChildList.map((e) => e.index).toList()}', + ); debugPrint( - 'displayingChildIndexList -- ${resultModel.displayingChildIndexList}'); + 'displayingChildIndexList -- ${resultModel.displayingChildIndexList}', + ); _tabController.index = ObserverUtils.calcAnchorTabIndex( observeModel: resultModel, @@ -76,6 +77,7 @@ class _AnchorWaterfallPageState extends State currentTabIndex: _tabController.index, ); }, + child: _buildGridView(), ), ); } @@ -92,8 +94,8 @@ class _AnchorWaterfallPageState extends State return Container( alignment: Alignment.center, color: Colors.teal[100 * (index % 9)], - child: Text('grid item $index'), height: 50.0 + 100.0 * (index % 9), + child: Text('grid item $index'), ); }, itemCount: 20, diff --git a/example/lib/features/scene/azlist_demo/azlist_cursor.dart b/example/lib/features/scene/azlist_demo/azlist_cursor.dart index 5fcff13..a4a5284 100644 --- a/example/lib/features/scene/azlist_demo/azlist_cursor.dart +++ b/example/lib/features/scene/azlist_demo/azlist_cursor.dart @@ -4,7 +4,7 @@ * @Date: 2023-10-28 15:56:01 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; class AzListCursor extends StatelessWidget { final double size; @@ -13,11 +13,7 @@ class AzListCursor extends StatelessWidget { final double arrowSize = 30; - const AzListCursor({ - Key? key, - required this.size, - required this.title, - }) : super(key: key); + const AzListCursor({super.key, required this.size, required this.title}); @override Widget build(BuildContext context) { diff --git a/example/lib/features/scene/azlist_demo/azlist_index_bar.dart b/example/lib/features/scene/azlist_demo/azlist_index_bar.dart index cf8cde5..dbcd7d6 100644 --- a/example/lib/features/scene/azlist_demo/azlist_index_bar.dart +++ b/example/lib/features/scene/azlist_demo/azlist_index_bar.dart @@ -4,7 +4,7 @@ * @Date: 2023-10-28 11:35:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; class AzListIndexBar extends StatefulWidget { @@ -12,20 +12,17 @@ class AzListIndexBar extends StatefulWidget { final List symbols; - final void Function( - int index, - Offset cursorOffset, - )? onSelectionUpdate; + final void Function(int index, Offset cursorOffset)? onSelectionUpdate; final void Function()? onSelectionEnd; const AzListIndexBar({ - Key? key, + super.key, required this.parentKey, required this.symbols, this.onSelectionUpdate, this.onSelectionEnd, - }) : super(key: key); + }); @override State createState() => _AzListIndexBarState(); @@ -41,9 +38,9 @@ class _AzListIndexBarState extends State { @override Widget build(BuildContext context) { Widget resultWidget = ListViewObserver( - child: _buildListView(), controller: observerController, dynamicLeadingOffset: () => observeOffset, + child: _buildListView(), ); resultWidget = GestureDetector( onVerticalDragUpdate: _onGestureHandler, @@ -82,8 +79,8 @@ class _AzListIndexBarState extends State { child: resultWidget, ); resultWidget = Align( - child: resultWidget, alignment: Alignment.centerLeft, + child: resultWidget, ); return resultWidget; }, @@ -93,7 +90,7 @@ class _AzListIndexBarState extends State { ); } - _onGestureHandler(dynamic details) async { + Future _onGestureHandler(dynamic details) async { if (details is! DragUpdateDetails && details is! DragDownDetails) return; observeOffset = details.localPosition.dy; @@ -119,13 +116,10 @@ class _AzListIndexBarState extends State { firstChildRenderObjOffset.dx, firstChildRenderObjOffset.dy + firstChildModel.size.width * 0.5, ); - widget.onSelectionUpdate?.call( - firstChildIndex, - cursorOffset, - ); + widget.onSelectionUpdate?.call(firstChildIndex, cursorOffset); } - _onGestureEnd([_]) { + void _onGestureEnd([_]) { selectedIndex.value = -1; widget.onSelectionEnd?.call(); } diff --git a/example/lib/features/scene/azlist_demo/azlist_item_view.dart b/example/lib/features/scene/azlist_demo/azlist_item_view.dart index f18b694..ece73e8 100644 --- a/example/lib/features/scene/azlist_demo/azlist_item_view.dart +++ b/example/lib/features/scene/azlist_demo/azlist_item_view.dart @@ -3,14 +3,14 @@ * @Repo: https://github.com/fluttercandies/flutter_scrollview_observer * @Date: 2023-10-28 19:43:04 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; class AzListItemView extends StatelessWidget { const AzListItemView({ - Key? key, + super.key, required this.name, this.isShowSeparator = true, - }) : super(key: key); + }); final String name; @@ -24,20 +24,12 @@ class AzListItemView extends StatelessWidget { height: 50, decoration: BoxDecoration( border: isShowSeparator - ? Border( - bottom: BorderSide( - color: Colors.grey[300]!, - width: 0.5, - ), - ) + ? Border(bottom: BorderSide(color: Colors.grey[300]!, width: 0.5)) : null, ), alignment: Alignment.centerLeft, margin: const EdgeInsets.only(left: 16.0), - child: Text( - name, - style: const TextStyle(color: Colors.black), - ), + child: Text(name, style: const TextStyle(color: Colors.black)), ), ); } diff --git a/example/lib/features/scene/azlist_demo/azlist_model.dart b/example/lib/features/scene/azlist_demo/azlist_model.dart index 1bb9620..e6fa76d 100644 --- a/example/lib/features/scene/azlist_demo/azlist_model.dart +++ b/example/lib/features/scene/azlist_demo/azlist_model.dart @@ -4,24 +4,18 @@ * @Date: 2023-10-28 10:19:23 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; class AzListContactModel { final String section; final List names; - AzListContactModel({ - required this.section, - required this.names, - }); + AzListContactModel({required this.section, required this.names}); } class AzListCursorInfoModel { final String title; final Offset offset; - AzListCursorInfoModel({ - required this.title, - required this.offset, - }); + AzListCursorInfoModel({required this.title, required this.offset}); } diff --git a/example/lib/features/scene/azlist_demo/azlist_page.dart b/example/lib/features/scene/azlist_demo/azlist_page.dart index b4ab6a5..3133f67 100644 --- a/example/lib/features/scene/azlist_demo/azlist_page.dart +++ b/example/lib/features/scene/azlist_demo/azlist_page.dart @@ -9,7 +9,7 @@ import 'dart:math'; import 'dart:typed_data'; import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/scene/azlist_demo/azlist_cursor.dart'; @@ -18,7 +18,7 @@ import 'package:scrollview_observer_example/features/scene/azlist_demo/azlist_mo import 'package:scrollview_observer_example/features/scene/azlist_demo/azlist_index_bar.dart'; class AzListPage extends StatefulWidget { - const AzListPage({Key? key}) : super(key: key); + const AzListPage({super.key}); @override State createState() => _AzListPageState(); @@ -43,7 +43,7 @@ class _AzListPageState extends State { Map sliverContextMap = {}; - generateContactData() { + void generateContactData() { final a = const Utf8Codec().encode("A").first; final z = const Utf8Codec().encode("Z").first; int pointer = a; @@ -97,12 +97,7 @@ class _AzListPageState extends State { ), ), _buildCursor(), - Positioned( - top: 0, - bottom: 0, - right: 0, - child: _buildIndexBar(), - ), + Positioned(top: 0, bottom: 0, right: 0, child: _buildIndexBar()), ], ), ); @@ -128,28 +123,25 @@ class _AzListPageState extends State { Widget _buildCursor() { return ValueListenableBuilder( valueListenable: cursorInfo, - builder: ( - BuildContext context, - AzListCursorInfoModel? value, - Widget? child, - ) { - Widget resultWidget = Container(); - double top = 0; - double right = indexBarWidth + 8; - if (value == null) { - resultWidget = const SizedBox.shrink(); - } else { - double titleSize = 80; - top = value.offset.dy - titleSize * 0.5; - resultWidget = AzListCursor(size: titleSize, title: value.title); - } - resultWidget = Positioned( - top: top, - right: right, - child: resultWidget, - ); - return resultWidget; - }, + builder: + (BuildContext context, AzListCursorInfoModel? value, Widget? child) { + Widget resultWidget = Container(); + double top = 0; + double right = indexBarWidth + 8; + if (value == null) { + resultWidget = const SizedBox.shrink(); + } else { + double titleSize = 80; + top = value.offset.dy - titleSize * 0.5; + resultWidget = AzListCursor(size: titleSize, title: value.title); + } + resultWidget = Positioned( + top: top, + right: right, + child: resultWidget, + ); + return resultWidget; + }, ); } @@ -168,10 +160,7 @@ class _AzListPageState extends State { ); final sliverContext = sliverContextMap[index]; if (sliverContext == null) return; - observerController.jumpTo( - index: 0, - sliverContext: sliverContext, - ); + observerController.jumpTo(index: 0, sliverContext: sliverContext); }, onSelectionEnd: () { cursorInfo.value = null; @@ -180,23 +169,17 @@ class _AzListPageState extends State { ); } - Widget _buildSliver({ - required int index, - required AzListContactModel model, - }) { + Widget _buildSliver({required int index, required AzListContactModel model}) { final names = model.names; if (names.isEmpty) return const SliverToBoxAdapter(); Widget resultWidget = isShowListMode ? SliverList( - delegate: SliverChildBuilderDelegate( - (context, itemIndex) { - if (sliverContextMap[index] == null) { - sliverContextMap[index] = context; - } - return AzListItemView(name: names[itemIndex]); - }, - childCount: names.length, - ), + delegate: SliverChildBuilderDelegate((context, itemIndex) { + if (sliverContextMap[index] == null) { + sliverContextMap[index] = context; + } + return AzListItemView(name: names[itemIndex]); + }, childCount: names.length), ) : SliverGrid( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( @@ -205,18 +188,18 @@ class _AzListPageState extends State { crossAxisSpacing: 10.0, childAspectRatio: 2.0, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int itemIndex) { - if (sliverContextMap[index] == null) { - sliverContextMap[index] = context; - } - return AzListItemView( - name: names[itemIndex], - isShowSeparator: false, - ); - }, - childCount: names.length, - ), + delegate: SliverChildBuilderDelegate(( + BuildContext context, + int itemIndex, + ) { + if (sliverContextMap[index] == null) { + sliverContextMap[index] = context; + } + return AzListItemView( + name: names[itemIndex], + isShowSeparator: false, + ); + }, childCount: names.length), ); resultWidget = SliverStickyHeader( header: Container( diff --git a/example/lib/features/scene/chat_demo/helper/chat_data_helper.dart b/example/lib/features/scene/chat_demo/helper/chat_data_helper.dart index 5ea5ceb..0976a7e 100644 --- a/example/lib/features/scene/chat_demo/helper/chat_data_helper.dart +++ b/example/lib/features/scene/chat_demo/helper/chat_data_helper.dart @@ -5,6 +5,7 @@ */ import 'dart:math'; + import 'package:scrollview_observer_example/features/scene/chat_demo/model/chat_model.dart'; class ChatDataHelper { @@ -17,18 +18,13 @@ class ChatDataHelper { 'Artile: Flutter-获取ListView当前正在显示的Widget信息\nhttps://juejin.cn/post/7103058155692621837', 'Artile: Flutter-列表滚动定位超强辅助库,墙裂推荐!🔥\nhttps://juejin.cn/post/7129888644290068487', 'A widget for observing data related to the child widgets being displayed in a scrollview.\nhttps://github.com/LinXunFeng/flutter_scrollview_observer', - '📱 Swifty screen adaptation solution (Support Objective-C and Swift)\nhttps://github.com/LinXunFeng/SwiftyFitsize' + '📱 Swifty screen adaptation solution (Support Objective-C and Swift)\nhttps://github.com/LinXunFeng/SwiftyFitsize', ]; - static ChatModel createChatModel({ - bool? isOwn, - }) { + static ChatModel createChatModel({bool? isOwn}) { final random = Random(); final content = ChatDataHelper.chatContents[random.nextInt(chatContents.length)]; - return ChatModel( - isOwn: isOwn ?? random.nextBool(), - content: content, - ); + return ChatModel(isOwn: isOwn ?? random.nextBool(), content: content); } } diff --git a/example/lib/features/scene/chat_demo/model/chat_model.dart b/example/lib/features/scene/chat_demo/model/chat_model.dart index 48a547e..7cfd1c0 100644 --- a/example/lib/features/scene/chat_demo/model/chat_model.dart +++ b/example/lib/features/scene/chat_demo/model/chat_model.dart @@ -5,10 +5,7 @@ */ class ChatModel { - ChatModel({ - required this.isOwn, - required this.content, - }); + ChatModel({required this.isOwn, required this.content}); final bool isOwn; final String content; } diff --git a/example/lib/features/scene/chat_demo/page/chat_gpt_page.dart b/example/lib/features/scene/chat_demo/page/chat_gpt_page.dart index f1565d4..edfce7f 100644 --- a/example/lib/features/scene/chat_demo/page/chat_gpt_page.dart +++ b/example/lib/features/scene/chat_demo/page/chat_gpt_page.dart @@ -6,14 +6,14 @@ import 'dart:async'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/scene/chat_demo/helper/chat_data_helper.dart'; import 'package:scrollview_observer_example/features/scene/chat_demo/model/chat_model.dart'; import 'package:scrollview_observer_example/features/scene/chat_demo/widget/chat_item_widget.dart'; class ChatGPTPage extends StatefulWidget { - const ChatGPTPage({Key? key}) : super(key: key); + const ChatGPTPage({super.key}); @override State createState() => _ChatGPTPageState(); @@ -75,7 +75,7 @@ class _ChatGPTPageState extends State { insertGenerativeMsg(); }, icon: const Icon(Icons.add_comment), - ) + ), ], ), body: _buildBody(), @@ -162,10 +162,7 @@ class _ChatGPTPageState extends State { controller: observerController, child: resultWidget, ); - resultWidget = Align( - child: resultWidget, - alignment: Alignment.topCenter, - ); + resultWidget = Align(alignment: Alignment.topCenter, child: resultWidget); return resultWidget; } @@ -175,16 +172,14 @@ class _ChatGPTPageState extends State { .toList(); } - _addMessage({ - required bool isOwn, - }) { + void _addMessage({required bool isOwn}) { chatObserver.standby(changeCount: 1); setState(() { chatModels.insert(0, ChatDataHelper.createChatModel(isOwn: isOwn)); }); } - insertGenerativeMsg() { + void insertGenerativeMsg() { stopMsgUpdateStream(); _addMessage(isOwn: false); int count = 0; @@ -226,7 +221,7 @@ class _ChatGPTPageState extends State { }); } - stopMsgUpdateStream() { + void stopMsgUpdateStream() { timer?.cancel(); timer = null; } diff --git a/example/lib/features/scene/chat_demo/page/chat_page.dart b/example/lib/features/scene/chat_demo/page/chat_page.dart index ab9fbbc..4b1ff77 100644 --- a/example/lib/features/scene/chat_demo/page/chat_page.dart +++ b/example/lib/features/scene/chat_demo/page/chat_page.dart @@ -5,7 +5,7 @@ */ import 'package:easy_refresh/easy_refresh.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/scene/chat_demo/helper/chat_data_helper.dart'; import 'package:scrollview_observer_example/features/scene/chat_demo/model/chat_model.dart'; @@ -15,7 +15,7 @@ import 'package:scrollview_observer_example/utils/keyboard.dart'; import 'package:scrollview_observer_example/utils/random.dart'; class ChatPage extends StatefulWidget { - const ChatPage({Key? key}) : super(key: key); + const ChatPage({super.key}); @override State createState() => _ChatPageState(); @@ -118,9 +118,7 @@ class _ChatPageState extends State with WidgetsBindingObserver { }, child: Text( isShowClassicHeaderAndFooter ? "Classic" : "Material", - style: const TextStyle( - fontSize: 18, - ), + style: const TextStyle(fontSize: 18), ), ), IconButton( @@ -129,7 +127,7 @@ class _ChatPageState extends State with WidgetsBindingObserver { _addMessage(RandomTool.genInt(min: 1, max: 3)); }, icon: const Icon(Icons.add_comment), - ) + ), ], ), body: _buildBody(), @@ -137,14 +135,16 @@ class _ChatPageState extends State with WidgetsBindingObserver { } Widget _buildPageOverlay() { - return Overlay(initialEntries: [ - OverlayEntry( - builder: (context) { - pageOverlayContext = context; - return Container(); - }, - ) - ]); + return Overlay( + initialEntries: [ + OverlayEntry( + builder: (context) { + pageOverlayContext = context; + return Container(); + }, + ), + ], + ); } Widget _buildBody() { @@ -163,18 +163,12 @@ class _ChatPageState extends State with WidgetsBindingObserver { resultWidget = Column( children: [ Expanded(child: resultWidget), - CompositedTransformTarget( - link: layerLink, - child: Container(), - ), + CompositedTransformTarget(link: layerLink, child: Container()), _buildEditView(), const SafeArea(top: false, child: SizedBox.shrink()), ], ); - resultWidget = Stack(children: [ - resultWidget, - _buildPageOverlay(), - ]); + resultWidget = Stack(children: [resultWidget, _buildPageOverlay()]); return resultWidget; } @@ -259,10 +253,9 @@ class _ChatPageState extends State with WidgetsBindingObserver { await Future.delayed(const Duration(seconds: 2)); }, childBuilder: (context, physics) { - var scrollViewPhysics = - physics.applyTo(ChatObserverClampingScrollPhysics( - observer: chatObserver, - )); + var scrollViewPhysics = physics.applyTo( + ChatObserverClampingScrollPhysics(observer: chatObserver), + ); Widget resultWidget = ListView.builder( physics: chatObserver.isShrinkWrap ? const NeverScrollableScrollPhysics() @@ -297,8 +290,8 @@ class _ChatPageState extends State with WidgetsBindingObserver { physics: scrollViewPhysics, child: Container( alignment: Alignment.topCenter, - child: resultWidget, height: constraints.maxHeight + 0.001, + child: resultWidget, ), ); } @@ -311,30 +304,32 @@ class _ChatPageState extends State with WidgetsBindingObserver { child: resultWidget, ); resultWidget = Align( - child: resultWidget, alignment: Alignment.topCenter, + child: resultWidget, ); return resultWidget; }, ); } - addUnreadTipView() { - Overlay.of(pageOverlayContext!).insert(OverlayEntry( - builder: (BuildContext context) => UnconstrainedBox( - child: CompositedTransformFollower( - link: layerLink, - followerAnchor: Alignment.bottomRight, - targetAnchor: Alignment.topRight, - offset: const Offset(-20, 0), - child: Material( - type: MaterialType.transparency, - // color: Colors.green, - child: _buildUnreadTipView(), + void addUnreadTipView() { + Overlay.of(pageOverlayContext!).insert( + OverlayEntry( + builder: (BuildContext context) => UnconstrainedBox( + child: CompositedTransformFollower( + link: layerLink, + followerAnchor: Alignment.bottomRight, + targetAnchor: Alignment.topRight, + offset: const Offset(-20, 0), + child: Material( + type: MaterialType.transparency, + // color: Colors.green, + child: _buildUnreadTipView(), + ), ), ), ), - )); + ); } List createChatModels({int num = 3}) { @@ -343,7 +338,7 @@ class _ChatPageState extends State with WidgetsBindingObserver { .toList(); } - _addMessage(int count) { + void _addMessage(int count) { chatObserver.standby(changeCount: count); setState(() { needIncrementUnreadMsgCount = true; @@ -353,10 +348,7 @@ class _ChatPageState extends State with WidgetsBindingObserver { }); } - updateUnreadMsgCount({ - bool isReset = false, - int changeCount = 1, - }) { + void updateUnreadMsgCount({bool isReset = false, int changeCount = 1}) { needIncrementUnreadMsgCount = false; if (isReset) { unreadMsgCount.value = 0; @@ -365,7 +357,7 @@ class _ChatPageState extends State with WidgetsBindingObserver { } } - scrollControllerListener() { + void scrollControllerListener() { if (scrollController.offset < 50) { updateUnreadMsgCount(isReset: true); } diff --git a/example/lib/features/scene/chat_demo/widget/chat_item_widget.dart b/example/lib/features/scene/chat_demo/widget/chat_item_widget.dart index cdd7ba3..9a436c4 100644 --- a/example/lib/features/scene/chat_demo/widget/chat_item_widget.dart +++ b/example/lib/features/scene/chat_demo/widget/chat_item_widget.dart @@ -4,17 +4,17 @@ * @Date: 2022-09-27 22:46:36 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/chat_demo/model/chat_model.dart'; class ChatItemWidget extends StatelessWidget { const ChatItemWidget({ - Key? key, + super.key, required this.chatModel, required this.index, required this.itemCount, this.onRemove, - }) : super(key: key); + }); final ChatModel chatModel; final int index; @@ -37,12 +37,7 @@ class ChatItemWidget extends StatelessWidget { color: isOwn ? Colors.blue : Colors.white30, ), child: Center( - child: Text( - nickName, - style: const TextStyle( - color: Colors.white, - ), - ), + child: Text(nickName, style: const TextStyle(color: Colors.white)), ), ), const SizedBox(width: 10), @@ -68,12 +63,7 @@ class ChatItemWidget extends StatelessWidget { const SizedBox(width: 50), ], ); - resultWidget = Column( - children: [ - resultWidget, - const SizedBox(height: 15), - ], - ); + resultWidget = Column(children: [resultWidget, const SizedBox(height: 15)]); resultWidget = Dismissible( key: UniqueKey(), child: resultWidget, diff --git a/example/lib/features/scene/chat_demo/widget/chat_unread_tip_view.dart b/example/lib/features/scene/chat_demo/widget/chat_unread_tip_view.dart index 2e66c0e..809fe75 100644 --- a/example/lib/features/scene/chat_demo/widget/chat_unread_tip_view.dart +++ b/example/lib/features/scene/chat_demo/widget/chat_unread_tip_view.dart @@ -4,14 +4,10 @@ * @Date: 2022-10-31 15:40:04 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; class ChatUnreadTipView extends StatelessWidget { - ChatUnreadTipView({ - Key? key, - required this.unreadMsgCount, - this.onTap, - }) : super(key: key); + ChatUnreadTipView({super.key, required this.unreadMsgCount, this.onTap}); final int unreadMsgCount; @@ -24,11 +20,7 @@ class ChatUnreadTipView extends StatelessWidget { if (unreadMsgCount == 0) return const SizedBox.shrink(); Widget resultWidget = Stack( children: [ - const Icon( - Icons.mode_comment, - size: 50, - color: Colors.white, - ), + const Icon(Icons.mode_comment, size: 50, color: Colors.white), Container( margin: const EdgeInsets.only(top: 12), width: 50, @@ -45,10 +37,7 @@ class ChatUnreadTipView extends StatelessWidget { ), ], ); - resultWidget = GestureDetector( - child: resultWidget, - onTap: onTap, - ); + resultWidget = GestureDetector(onTap: onTap, child: resultWidget); return resultWidget; } } diff --git a/example/lib/features/scene/detail/header/detail_header.dart b/example/lib/features/scene/detail/header/detail_header.dart index 79c9827..4e0e7ce 100644 --- a/example/lib/features/scene/detail/header/detail_header.dart +++ b/example/lib/features/scene/detail/header/detail_header.dart @@ -4,23 +4,17 @@ * @Date: 2025-08-02 19:57:05 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:getx_helper/getx_helper.dart'; import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic.dart'; -typedef DetailLogicPutMixin - = GetxLogicPutStateMixin; +typedef DetailLogicPutMixin = + GetxLogicPutStateMixin; -typedef DetailLogicConsumerMixin - = GetxLogicConsumerStateMixin; +typedef DetailLogicConsumerMixin = + GetxLogicConsumerStateMixin; -enum DetailUpdateType { - navBar, - config, - loading, - module3, - module6, -} +enum DetailUpdateType { navBar, config, loading, module3, module6 } enum DetailModuleType { module1, @@ -33,7 +27,4 @@ enum DetailModuleType { module8, } -enum DetailRefreshIndicatorType { - none, - footer, -} +enum DetailRefreshIndicatorType { none, footer } diff --git a/example/lib/features/scene/detail/logic/detail_logic_list_view.dart b/example/lib/features/scene/detail/logic/detail_logic_list_view.dart index ca50528..e5d8e09 100644 --- a/example/lib/features/scene/detail/logic/detail_logic_list_view.dart +++ b/example/lib/features/scene/detail/logic/detail_logic_list_view.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-03 21:02:30 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/common/route/route.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; @@ -37,9 +37,7 @@ extension DetailLogicForListView on DetailLogic { } void initIndexPositionForListView() { - final defaultIndexModel = ObserverIndexPositionModel( - index: 0, - ); + final defaultIndexModel = ObserverIndexPositionModel(index: 0); ObserverIndexPositionModel indexModel = defaultIndexModel; () { @@ -75,9 +73,7 @@ extension DetailLogicForListView on DetailLogic { } /// When updating the module widget, keep the current position unchanged. - void updateAndKeepPositionForListView([ - List? ids, - ]) { + void updateAndKeepPositionForListView([List? ids]) { () async { final result = await state.observerController.dispatchOnceObserve( isForce: true, @@ -115,8 +111,10 @@ extension DetailLogicForListView on DetailLogic { // update([DetailUpdateType.module3]); updateAndKeepPositionForListView([DetailUpdateType.module3]); checkAnchorForListView(DetailModuleType.module3); + final context = NavigationService.context; + if (!context.mounted) return; SnackBarUtil.showSnackBar( - context: NavigationService.context, + context: context, text: 'Module3 has been displayed', ); }); @@ -127,8 +125,10 @@ extension DetailLogicForListView on DetailLogic { // update([DetailUpdateType.module6]); updateAndKeepPositionForListView([DetailUpdateType.module6]); checkAnchorForListView(DetailModuleType.module6); + final context = NavigationService.context; + if (!context.mounted) return; SnackBarUtil.showSnackBar( - context: NavigationService.context, + context: context, text: 'Module6 has been displayed', ); }); diff --git a/example/lib/features/scene/detail/logic/detail_logic_nav_bar.dart b/example/lib/features/scene/detail/logic/detail_logic_nav_bar.dart index 742fa94..c2c9071 100644 --- a/example/lib/features/scene/detail/logic/detail_logic_nav_bar.dart +++ b/example/lib/features/scene/detail/logic/detail_logic_nav_bar.dart @@ -6,7 +6,7 @@ import 'dart:math'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic.dart'; import 'package:scrollview_observer_example/features/scene/detail/model/detail_nav_bar_tab_model.dart'; @@ -24,9 +24,7 @@ extension DetailLogicForNavBar on DetailLogic { ); } - DetailNavBarTabModel createNavBarTabModel( - DetailModuleType type, - ) { + DetailNavBarTabModel createNavBarTabModel(DetailModuleType type) { return DetailNavBarTabModel( type: type, index: state.moduleTypes.indexOf(type), @@ -70,15 +68,10 @@ extension DetailLogicForNavBar on DetailLogic { final navBarHeight = state.navBarHeight; double newAlpha = 0.0; - newAlpha = min( - 1.0, - max(0.0, scrollOffset / navBarHeight), - ); + newAlpha = min(1.0, max(0.0, scrollOffset / navBarHeight)); if (state.navBarAlpha == newAlpha) return; state.navBarAlpha = newAlpha; - update([ - DetailUpdateType.navBar, - ]); + update([DetailUpdateType.navBar]); } } diff --git a/example/lib/features/scene/detail/model/detail_nav_bar_tab_model.dart b/example/lib/features/scene/detail/model/detail_nav_bar_tab_model.dart index 432707d..8a324fc 100644 --- a/example/lib/features/scene/detail/model/detail_nav_bar_tab_model.dart +++ b/example/lib/features/scene/detail/model/detail_nav_bar_tab_model.dart @@ -13,8 +13,5 @@ class DetailNavBarTabModel { String get title => '${type.name[0].toUpperCase()}${type.name.substring(1)}'; - DetailNavBarTabModel({ - required this.type, - required this.index, - }); + DetailNavBarTabModel({required this.type, required this.index}); } diff --git a/example/lib/features/scene/detail/page/detail_page.dart b/example/lib/features/scene/detail/page/detail_page.dart index e2d6b8b..5d9beba 100644 --- a/example/lib/features/scene/detail/page/detail_page.dart +++ b/example/lib/features/scene/detail/page/detail_page.dart @@ -4,8 +4,8 @@ * @Date: 2025-08-02 19:57:05 */ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; +import 'package:cupertino_ui/cupertino_ui.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic.dart'; @@ -40,18 +40,13 @@ class DetailPageState extends State tag: logicTag, assignId: true, builder: (_) { - return Scaffold( - appBar: _buildAppBar(), - body: _buildBody(), - ); + return Scaffold(appBar: _buildAppBar(), body: _buildBody()); }, ); } AppBar _buildAppBar() { - return AppBar( - title: const Text('Detail Page'), - ); + return AppBar(title: const Text('Detail Page')); } Widget _buildBody() { @@ -61,12 +56,7 @@ class DetailPageState extends State return Stack( children: [ const DetailListView(), - const Positioned( - top: 0, - left: 0, - right: 0, - child: DetailNavBar(), - ), + const Positioned(top: 0, left: 0, right: 0, child: DetailNavBar()), _buildLoading(), ], ); diff --git a/example/lib/features/scene/detail/state/detail_state_config.dart b/example/lib/features/scene/detail/state/detail_state_config.dart index 9e617a5..924a4fa 100644 --- a/example/lib/features/scene/detail/state/detail_state_config.dart +++ b/example/lib/features/scene/detail/state/detail_state_config.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-05 22:42:42 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; mixin DetailStateForConfig { @@ -15,12 +15,7 @@ mixin DetailStateForConfig { List> get configDefaultAnchorEntries { List> entries = []; for (final moduleType in DetailModuleType.values) { - entries.add( - DropdownMenuEntry( - value: moduleType, - label: moduleType.name, - ), - ); + entries.add(DropdownMenuEntry(value: moduleType, label: moduleType.name)); } return entries; } @@ -29,15 +24,10 @@ mixin DetailStateForConfig { DetailRefreshIndicatorType.none; List> - get configRefreshIndicatorEntries { + get configRefreshIndicatorEntries { List> entries = []; for (final moduleType in DetailRefreshIndicatorType.values) { - entries.add( - DropdownMenuEntry( - value: moduleType, - label: moduleType.name, - ), - ); + entries.add(DropdownMenuEntry(value: moduleType, label: moduleType.name)); } return entries; } diff --git a/example/lib/features/scene/detail/state/detail_state_list_view.dart b/example/lib/features/scene/detail/state/detail_state_list_view.dart index 721a2a0..8e47f61 100644 --- a/example/lib/features/scene/detail/state/detail_state_list_view.dart +++ b/example/lib/features/scene/detail/state/detail_state_list_view.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-03 22:01:50 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; @@ -15,13 +15,12 @@ mixin DetailStateForListView { ScrollController scrollController = ScrollController(); - late ListObserverController observerController = ListObserverController( - controller: scrollController, - ) - ..observeIntervalForScrolling = const Duration(milliseconds: 50) - // Since there are modules loaded asynchronously, which will cause the - // cache to be offset inaccurately, so it is set to false here - ..cacheJumpIndexOffset = false; + late ListObserverController observerController = + ListObserverController(controller: scrollController) + ..observeIntervalForScrolling = const Duration(milliseconds: 50) + // Since there are modules loaded asynchronously, which will cause the + // cache to be offset inaccurately, so it is set to false here + ..cacheJumpIndexOffset = false; late ChatScrollObserver keepPositionObserver = ChatScrollObserver( observerController, diff --git a/example/lib/features/scene/detail/state/detail_state_nav_bar.dart b/example/lib/features/scene/detail/state/detail_state_nav_bar.dart index 1327b41..1b49128 100644 --- a/example/lib/features/scene/detail/state/detail_state_nav_bar.dart +++ b/example/lib/features/scene/detail/state/detail_state_nav_bar.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-03 21:08:34 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/detail/model/detail_nav_bar_tab_model.dart'; mixin DetailStateForNavBar { diff --git a/example/lib/features/scene/detail/widget/detail_config_view.dart b/example/lib/features/scene/detail/widget/detail_config_view.dart index 287f5ea..f47bc51 100644 --- a/example/lib/features/scene/detail/widget/detail_config_view.dart +++ b/example/lib/features/scene/detail/widget/detail_config_view.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-10 12:50:51 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic.dart'; @@ -30,28 +30,18 @@ class _DetailConfigViewState extends State builder: (_) { Widget resultWidget = Column( children: [ - Expanded( - child: _buildListView(), - ), + Expanded(child: _buildListView()), _buildConfirmBtn(), ], ); - resultWidget = SafeArea( - top: false, - child: resultWidget, - ); + resultWidget = SafeArea(top: false, child: resultWidget); return resultWidget; }, ); } Widget _buildListView() { - return ListView( - children: [ - _buildDefaultAnchor(), - _buildRefreshPosition(), - ], - ); + return ListView(children: [_buildDefaultAnchor(), _buildRefreshPosition()]); } Widget _buildDefaultAnchor() { @@ -103,8 +93,6 @@ class _DetailConfigViewState extends State } InputDecorationTheme inputDecorationTheme() { - return const InputDecorationTheme( - isCollapsed: true, - ); + return const InputDecorationTheme(isCollapsed: true); } } diff --git a/example/lib/features/scene/detail/widget/detail_list_item_wrapper.dart b/example/lib/features/scene/detail/widget/detail_list_item_wrapper.dart index 1a477d1..4619214 100644 --- a/example/lib/features/scene/detail/widget/detail_list_item_wrapper.dart +++ b/example/lib/features/scene/detail/widget/detail_list_item_wrapper.dart @@ -4,18 +4,14 @@ * @Date: 2025-08-03 15:07:52 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; class DetailListItemWrapper extends StatefulWidget { final String? title; final Widget child; - const DetailListItemWrapper({ - super.key, - required this.child, - this.title, - }); + const DetailListItemWrapper({super.key, required this.child, this.title}); @override State createState() => _DetailListItemWrapperState(); @@ -29,11 +25,7 @@ class _DetailListItemWrapperState extends State Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildTitle(), - widget.child, - const SizedBox(height: 10), - ], + children: [_buildTitle(), widget.child, const SizedBox(height: 10)], ); } @@ -45,11 +37,7 @@ class _DetailListItemWrapperState extends State style: Theme.of(context).textTheme.titleLarge, ); resultWidget = Padding( - padding: const EdgeInsets.only( - top: 16, - left: 8, - bottom: 8, - ), + padding: const EdgeInsets.only(top: 16, left: 8, bottom: 8), child: resultWidget, ); return resultWidget; diff --git a/example/lib/features/scene/detail/widget/detail_list_view.dart b/example/lib/features/scene/detail/widget/detail_list_view.dart index 47c6373..9694dc6 100644 --- a/example/lib/features/scene/detail/widget/detail_list_view.dart +++ b/example/lib/features/scene/detail/widget/detail_list_view.dart @@ -5,7 +5,7 @@ */ import 'package:easy_refresh/easy_refresh.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic_list_view.dart'; @@ -56,19 +56,17 @@ class _DetailListViewState extends State return resultWidget; } - Widget _buildListView({ - ScrollPhysics? physics, - }) { - ScrollPhysics _physics = ChatObserverClampingScrollPhysics( + Widget _buildListView({ScrollPhysics? physics}) { + ScrollPhysics resultPhysics = ChatObserverClampingScrollPhysics( observer: state.keepPositionObserver, ); if (physics != null) { - _physics = physics.applyTo(_physics); + resultPhysics = physics.applyTo(resultPhysics); } Widget resultWidget = ListView.separated( controller: state.scrollController, - physics: _physics, + physics: resultPhysics, itemBuilder: (context, index) { switch (moduleTypes[index]) { case DetailModuleType.module1: @@ -90,10 +88,7 @@ class _DetailListViewState extends State } }, separatorBuilder: (context, index) { - return Container( - color: Colors.black12, - height: 1, - ); + return Container(color: Colors.black12, height: 1); }, itemCount: moduleTypes.length, // Set a large enough cacheExtent to ensure that the keep position diff --git a/example/lib/features/scene/detail/widget/detail_nav_bar.dart b/example/lib/features/scene/detail/widget/detail_nav_bar.dart index b80aa20..0c109d5 100644 --- a/example/lib/features/scene/detail/widget/detail_nav_bar.dart +++ b/example/lib/features/scene/detail/widget/detail_nav_bar.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-03 16:59:04 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic.dart'; @@ -50,10 +50,7 @@ class _DetailNavBarState extends State ), child: _buildTabBar(), ); - resultWidget = Opacity( - opacity: state.navBarAlpha, - child: resultWidget, - ); + resultWidget = Opacity(opacity: state.navBarAlpha, child: resultWidget); resultWidget = IgnorePointer( ignoring: state.navBarAlpha < 0.2, child: resultWidget, @@ -74,10 +71,7 @@ class _DetailNavBarState extends State indicatorSize: TabBarIndicatorSize.label, labelColor: Colors.black, unselectedLabelColor: Colors.grey[600], - labelStyle: const TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.bold, - ), + labelStyle: const TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold), unselectedLabelStyle: const TextStyle(fontSize: 14.0), labelPadding: const EdgeInsets.only(right: 15), padding: const EdgeInsets.symmetric(horizontal: 8), diff --git a/example/lib/features/scene/detail/widget/list_item/detail_list_module1.dart b/example/lib/features/scene/detail/widget/list_item/detail_list_module1.dart index 2b76ca0..1031262 100644 --- a/example/lib/features/scene/detail/widget/list_item/detail_list_module1.dart +++ b/example/lib/features/scene/detail/widget/list_item/detail_list_module1.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-02 21:32:17 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/widget/detail_list_item_wrapper.dart'; @@ -20,9 +20,7 @@ class _DetailListModule1State extends State @override Widget build(BuildContext context) { Widget resultWidget = _buildPageView(); - resultWidget = DetailListItemWrapper( - child: resultWidget, - ); + resultWidget = DetailListItemWrapper(child: resultWidget); return resultWidget; } @@ -33,10 +31,7 @@ class _DetailListModule1State extends State return _buildPageItem(index); }, ); - resultWidget = SizedBox( - height: 200, - child: resultWidget, - ); + resultWidget = SizedBox(height: 200, child: resultWidget); return resultWidget; } @@ -44,11 +39,7 @@ class _DetailListModule1State extends State Widget resultWidget = Stack( children: [ _buildPageItemBody(index), - Positioned( - top: 16, - right: 16, - child: _buildPageItemIndex(index), - ), + Positioned(top: 16, right: 16, child: _buildPageItemIndex(index)), ], ); resultWidget = Container( @@ -109,16 +100,10 @@ class _DetailListModule1State extends State Widget _buildPageItemIndex(int index) { Widget resultWidget = Text( '${index + 1}/5', - style: const TextStyle( - color: Colors.white, - fontSize: 12, - ), + style: const TextStyle(color: Colors.white, fontSize: 12), ); resultWidget = Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 4, - ), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.black, borderRadius: BorderRadius.circular(8), diff --git a/example/lib/features/scene/detail/widget/list_item/detail_list_module2.dart b/example/lib/features/scene/detail/widget/list_item/detail_list_module2.dart index bc190cd..29b35b0 100644 --- a/example/lib/features/scene/detail/widget/list_item/detail_list_module2.dart +++ b/example/lib/features/scene/detail/widget/list_item/detail_list_module2.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-02 21:32:08 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/widget/detail_list_item_wrapper.dart'; @@ -20,10 +20,7 @@ class _DetailListModule2State extends State @override Widget build(BuildContext context) { Widget resultWidget = _buildListView(); - resultWidget = SizedBox( - height: 300, - child: resultWidget, - ); + resultWidget = SizedBox(height: 300, child: resultWidget); resultWidget = DetailListItemWrapper( title: 'Module 2', child: resultWidget, @@ -52,11 +49,7 @@ class _DetailListModule2State extends State Stack( children: [ _buildItemCover(index), - Positioned( - top: 8, - right: 8, - child: _buildItemFavoriteIcon(), - ), + Positioned(top: 8, right: 8, child: _buildItemFavoriteIcon()), ], ), Padding( @@ -70,25 +63,17 @@ class _DetailListModule2State extends State const SizedBox(height: 40), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _buildItemPrice(), - _buildItemAddBtn(), - ], + children: [_buildItemPrice(), _buildItemAddBtn()], ), ], ), ), ], ); - resultWidget = SizedBox( - width: 200, - child: resultWidget, - ); + resultWidget = SizedBox(width: 200, child: resultWidget); resultWidget = Card( margin: const EdgeInsets.symmetric(vertical: 5), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.0)), elevation: 2, child: resultWidget, ); @@ -98,20 +83,14 @@ class _DetailListModule2State extends State Widget _buildItemBrand() { return const Text( 'NIKE', - style: TextStyle( - color: Colors.grey, - fontSize: 12, - ), + style: TextStyle(color: Colors.grey, fontSize: 12), ); } Widget _buildItemTitle() { return const Text( 'Air Force 1', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ); } @@ -121,21 +100,14 @@ class _DetailListModule2State extends State color: Colors.blue, borderRadius: BorderRadius.circular(12), ), - child: const Icon( - Icons.add, - color: Colors.white, - size: 24, - ), + child: const Icon(Icons.add, color: Colors.white, size: 24), ); } Widget _buildItemPrice() { return const Text( '\$90.00', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ); } @@ -163,9 +135,7 @@ class _DetailListModule2State extends State clipBehavior: Clip.hardEdge, decoration: BoxDecoration( color: Colors.green.shade50, - borderRadius: const BorderRadius.vertical( - top: Radius.circular(16.0), - ), + borderRadius: const BorderRadius.vertical(top: Radius.circular(16.0)), ), ); } diff --git a/example/lib/features/scene/detail/widget/list_item/detail_list_module3.dart b/example/lib/features/scene/detail/widget/list_item/detail_list_module3.dart index 1d5b2da..56af2a7 100644 --- a/example/lib/features/scene/detail/widget/list_item/detail_list_module3.dart +++ b/example/lib/features/scene/detail/widget/list_item/detail_list_module3.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-02 21:32:21 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic.dart'; @@ -94,10 +94,7 @@ class _DetailListModule3State extends State Widget _buildItemTitle(int index) { return Text( titles[index], - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), + style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), textAlign: TextAlign.center, ); } diff --git a/example/lib/features/scene/detail/widget/list_item/detail_list_module4.dart b/example/lib/features/scene/detail/widget/list_item/detail_list_module4.dart index 06df3c8..aa0eb42 100644 --- a/example/lib/features/scene/detail/widget/list_item/detail_list_module4.dart +++ b/example/lib/features/scene/detail/widget/list_item/detail_list_module4.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-02 21:30:01 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/widget/detail_list_item_wrapper.dart'; @@ -58,10 +58,7 @@ class _DetailListModule4State extends State ], ); resultWidget = Container( - margin: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, - ), + margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: Colors.white, @@ -83,20 +80,14 @@ class _DetailListModule4State extends State Widget _buildItemSubtitle(int index) { return Text( 'Software Engineer - Company ${index + 1}', - style: const TextStyle( - color: Colors.grey, - fontSize: 14, - ), + style: const TextStyle(color: Colors.grey, fontSize: 14), ); } Widget _buildItemTitle(int index) { return Text( 'User Name ${index + 1}', - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ); } @@ -104,18 +95,11 @@ class _DetailListModule4State extends State return CircleAvatar( radius: 30, backgroundColor: Colors.blue.shade100, - child: Icon( - Icons.person, - size: 30, - color: Colors.blue.shade800, - ), + child: Icon(Icons.person, size: 30, color: Colors.blue.shade800), ); } Widget _buildItemArrow() { - return Icon( - Icons.arrow_forward_ios, - color: Colors.grey.shade400, - ); + return Icon(Icons.arrow_forward_ios, color: Colors.grey.shade400); } } diff --git a/example/lib/features/scene/detail/widget/list_item/detail_list_module5.dart b/example/lib/features/scene/detail/widget/list_item/detail_list_module5.dart index 4929133..c0ff367 100644 --- a/example/lib/features/scene/detail/widget/list_item/detail_list_module5.dart +++ b/example/lib/features/scene/detail/widget/list_item/detail_list_module5.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-02 21:32:26 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/widget/detail_list_item_wrapper.dart'; @@ -94,9 +94,6 @@ class _DetailListModule5State extends State } Widget _getTrailingWidget(int index) { - return Switch( - value: index % 2 == 0, - onChanged: (_) {}, - ); + return Switch(value: index % 2 == 0, onChanged: (_) {}); } } diff --git a/example/lib/features/scene/detail/widget/list_item/detail_list_module6.dart b/example/lib/features/scene/detail/widget/list_item/detail_list_module6.dart index e8ca7f9..b47f1fe 100644 --- a/example/lib/features/scene/detail/widget/list_item/detail_list_module6.dart +++ b/example/lib/features/scene/detail/widget/list_item/detail_list_module6.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-02 21:32:31 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:get/get.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/logic/detail_logic.dart'; @@ -49,10 +49,7 @@ class _DetailListModule6State extends State return _buildCardItem(index); }, ); - resultWidget = SizedBox( - height: 220, - child: resultWidget, - ); + resultWidget = SizedBox(height: 220, child: resultWidget); return resultWidget; } @@ -71,21 +68,13 @@ class _DetailListModule6State extends State ), ], ); - resultWidget = SizedBox( - width: 180, - child: resultWidget, - ); + resultWidget = SizedBox(width: 180, child: resultWidget); resultWidget = Card( elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - child: resultWidget, - ); - resultWidget = Padding( - padding: EdgeInsets.zero, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: resultWidget, ); + resultWidget = Padding(padding: EdgeInsets.zero, child: resultWidget); return resultWidget; } @@ -95,9 +84,7 @@ class _DetailListModule6State extends State width: double.infinity, decoration: BoxDecoration( color: Colors.blueGrey.shade100, - borderRadius: const BorderRadius.vertical( - top: Radius.circular(12), - ), + borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), ), ); } @@ -105,10 +92,7 @@ class _DetailListModule6State extends State Widget _buildCardItemSubtitle(int index) { Widget resultWidget = Text( 'Description for item ${index + 1}.', - style: const TextStyle( - color: Colors.grey, - fontSize: 12, - ), + style: const TextStyle(color: Colors.grey, fontSize: 12), maxLines: 2, overflow: TextOverflow.ellipsis, ); @@ -122,10 +106,7 @@ class _DetailListModule6State extends State Widget _buildCardItemTitle(int index) { Widget resultWidget = Text( 'Title ${index + 1}', - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), maxLines: 1, overflow: TextOverflow.ellipsis, ); diff --git a/example/lib/features/scene/detail/widget/list_item/detail_list_module7.dart b/example/lib/features/scene/detail/widget/list_item/detail_list_module7.dart index c8e2250..5d41cac 100644 --- a/example/lib/features/scene/detail/widget/list_item/detail_list_module7.dart +++ b/example/lib/features/scene/detail/widget/list_item/detail_list_module7.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-02 21:32:39 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/widget/detail_list_item_wrapper.dart'; @@ -68,9 +68,7 @@ class _DetailListModule7State extends State resultWidget = Card( margin: const EdgeInsets.symmetric(vertical: 8), elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: resultWidget, ); return resultWidget; @@ -89,20 +87,14 @@ class _DetailListModule7State extends State Widget _buildListItemProgress(double progress) { return Text( '${(progress * 100).toInt()}% Done', - style: const TextStyle( - color: Colors.grey, - fontSize: 14, - ), + style: const TextStyle(color: Colors.grey, fontSize: 14), ); } Widget _buildListItemTitle(int index) { return Text( 'Task ${index + 1}: Data Sync', - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ); } } diff --git a/example/lib/features/scene/detail/widget/list_item/detail_list_module8.dart b/example/lib/features/scene/detail/widget/list_item/detail_list_module8.dart index e3aaacf..00208dd 100644 --- a/example/lib/features/scene/detail/widget/list_item/detail_list_module8.dart +++ b/example/lib/features/scene/detail/widget/list_item/detail_list_module8.dart @@ -4,7 +4,7 @@ * @Date: 2025-08-02 21:32:45 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/detail/header/detail_header.dart'; import 'package:scrollview_observer_example/features/scene/detail/widget/detail_list_item_wrapper.dart'; @@ -47,10 +47,7 @@ class _DetailListModule8State extends State Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildItemDetails(), - const SizedBox(height: 16), - ], + children: [_buildItemDetails(), const SizedBox(height: 16)], ), ), ], @@ -60,14 +57,9 @@ class _DetailListModule8State extends State child: resultWidget, ); resultWidget = Card( - margin: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 8, - ), + margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), elevation: 2, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.0)), child: resultWidget, ); return resultWidget; @@ -90,26 +82,14 @@ class _DetailListModule8State extends State children: [ Text( 'Air Jordan 1 Low SE', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ), SizedBox(height: 4), - Text( - 'NIKE', - style: TextStyle( - color: Colors.grey, - fontSize: 12, - ), - ), + Text('NIKE', style: TextStyle(color: Colors.grey, fontSize: 12)), SizedBox(height: 4), Text( '\$120.00', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - ), + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14), ), ], ); diff --git a/example/lib/features/scene/expandable_carousel_slider_demo/expandable_carousel_slider_demo.dart b/example/lib/features/scene/expandable_carousel_slider_demo/expandable_carousel_slider_demo.dart index 05a5b85..3f2bb2b 100644 --- a/example/lib/features/scene/expandable_carousel_slider_demo/expandable_carousel_slider_demo.dart +++ b/example/lib/features/scene/expandable_carousel_slider_demo/expandable_carousel_slider_demo.dart @@ -4,7 +4,7 @@ * @Date: 2024-11-25 20:15:18 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:carousel_slider/carousel_slider.dart'; @@ -14,7 +14,7 @@ import 'package:collection/collection.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; class ExpandableCarouselSliderDemo extends StatefulWidget { - const ExpandableCarouselSliderDemo({Key? key}) : super(key: key); + const ExpandableCarouselSliderDemo({super.key}); @override State createState() => @@ -64,9 +64,7 @@ class _ExpandableCarouselSliderDemoState @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('Expandable Carousel Slider'), - ), + appBar: AppBar(title: const Text('Expandable Carousel Slider')), body: Column( children: [ _buildCarousel(), @@ -88,9 +86,7 @@ class _ExpandableCarouselSliderDemoState ), child: const Text( 'I am an indicator', - style: TextStyle( - color: Colors.white, - ), + style: TextStyle(color: Colors.white), ), ); } @@ -115,7 +111,6 @@ class _ExpandableCarouselSliderDemoState resultWidget = ListViewObserver( controller: observerController, - child: resultWidget, triggerOnObserveType: ObserverTriggerOnObserveType.directly, customTargetRenderSliverType: (renderObj) { return renderObj is RenderSliverFillViewport; @@ -149,9 +144,11 @@ class _ExpandableCarouselSliderDemoState final progress = (firstChildLeadingMarginToViewport.abs() / viewportMainAxisExtent) .clamp(0.0, 1.0); - carouselHeight.value = firstChildHeight - + carouselHeight.value = + firstChildHeight - ((firstChildHeight - secondChildHeight) * progress); }, + child: resultWidget, ); return resultWidget; } @@ -159,11 +156,11 @@ class _ExpandableCarouselSliderDemoState class CarouselItem extends StatelessWidget { const CarouselItem({ - Key? key, + super.key, required this.index, required this.height, required this.imgId, - }) : super(key: key); + }); final int index; final double height; @@ -180,11 +177,11 @@ class CarouselItem extends StatelessWidget { child: Container( width: double.infinity, height: height, + color: index % 2 == 0 ? Colors.red : Colors.amber, child: Image.network( // 'https://picsum.photos/${MediaQuery.sizeOf(context).width.toInt()}/${height.toInt()}?random=$index', 'https://images.unsplash.com/$imgId?auto=format&fit=crop&w=${MediaQuery.sizeOf(context).width.toInt()}&h=${height.toInt()}&q=100', ), - color: index % 2 == 0 ? Colors.red : Colors.amber, ), ), ], diff --git a/example/lib/features/scene/image_tab_demo/image_tab_page.dart b/example/lib/features/scene/image_tab_demo/image_tab_page.dart index 2cd9635..6f6fbd2 100644 --- a/example/lib/features/scene/image_tab_demo/image_tab_page.dart +++ b/example/lib/features/scene/image_tab_demo/image_tab_page.dart @@ -4,11 +4,11 @@ * @Date: 2022-08-22 22:17:55 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; class ImageTabPage extends StatefulWidget { - const ImageTabPage({Key? key}) : super(key: key); + const ImageTabPage({super.key}); @override State createState() => _ImageTabPageState(); @@ -85,10 +85,7 @@ class _ImageTabPageState extends State { scrollDirection: Axis.horizontal, ), ); - resultWidget = SizedBox( - height: 100, - child: resultWidget, - ); + resultWidget = SizedBox(height: 100, child: resultWidget); return resultWidget; } @@ -140,7 +137,7 @@ class _ImageTabPageState extends State { child: CircularProgressIndicator( value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! + loadingProgress.expectedTotalBytes! : null, ), ); @@ -149,15 +146,10 @@ class _ImageTabPageState extends State { } Container _buildSeparatorView() { - return Container( - color: Colors.white, - width: 5, - ); + return Container(color: Colors.white, width: 5); } String _fetchImgUrl(int index) { - return 'https://images.unsplash.com/' + - imgUrlList[index] + - '?auto=format&fit=crop&w=375&q=100'; + return 'https://images.unsplash.com/${imgUrlList[index]}?auto=format&fit=crop&w=375&q=100'; } } diff --git a/example/lib/features/scene/scrollview_form_demo/scrollview_form_demo_page.dart b/example/lib/features/scene/scrollview_form_demo/scrollview_form_demo_page.dart index ded32b3..3577b3f 100644 --- a/example/lib/features/scene/scrollview_form_demo/scrollview_form_demo_page.dart +++ b/example/lib/features/scene/scrollview_form_demo/scrollview_form_demo_page.dart @@ -4,12 +4,12 @@ * @Date: 2023-08-10 22:35:59 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/utils/random.dart'; class ScrollViewFormDemoPage extends StatefulWidget { - const ScrollViewFormDemoPage({Key? key}) : super(key: key); + const ScrollViewFormDemoPage({super.key}); @override State createState() => _ScrollViewFormDemoPageState(); @@ -35,7 +35,7 @@ class _ScrollViewFormDemoPageState extends State { 'photo-1556799483-e642a45aeb68', ]; - handleFormFocus() async { + Future handleFormFocus() async { if (!formFocusNode.hasFocus) return; // Wait for the keyboard to fully display. await Future.delayed(const Duration(milliseconds: 600)); @@ -47,10 +47,10 @@ class _ScrollViewFormDemoPageState extends State { if (!result.isSuccess) return; // Find the observation result for the form item. - final formResultModel = - result.observeResult?.displayingChildModelList.firstWhere((element) { - return element.index == formIndex; - }); + final formResultModel = result.observeResult?.displayingChildModelList + .firstWhere((element) { + return element.index == formIndex; + }); if (formResultModel == null) return; // Let the bottom of the form item view be fully displayed. observerController.controller?.animateTo( @@ -109,14 +109,9 @@ class _ScrollViewFormDemoPageState extends State { children: [ const Text( 'Feedback', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - TextField( - focusNode: formFocusNode, + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), + TextField(focusNode: formFocusNode), Container( width: double.infinity, color: Colors.white, @@ -151,7 +146,7 @@ class _ScrollViewFormDemoPageState extends State { child: CircularProgressIndicator( value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! + loadingProgress.expectedTotalBytes! : null, ), ); @@ -165,8 +160,6 @@ class _ScrollViewFormDemoPageState extends State { } String _fetchImgUrl() { - return 'https://images.unsplash.com/' + - imgUrlList[RandomTool.genInt(max: imgUrlList.length)] + - '?auto=format&fit=crop&w=375&q=100'; + return 'https://images.unsplash.com/${imgUrlList[RandomTool.genInt(max: imgUrlList.length)]}?auto=format&fit=crop&w=375&q=100'; } } diff --git a/example/lib/features/scene/video_auto_play_list/video_list_auto_play_page.dart b/example/lib/features/scene/video_auto_play_list/video_list_auto_play_page.dart index 376ab40..e25113d 100644 --- a/example/lib/features/scene/video_auto_play_list/video_list_auto_play_page.dart +++ b/example/lib/features/scene/video_auto_play_list/video_list_auto_play_page.dart @@ -3,12 +3,12 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-07-03 15:46:45 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/scene/video_auto_play_list/widgets/video_widget.dart'; class VideoListAutoPlayPage extends StatefulWidget { - const VideoListAutoPlayPage({Key? key}) : super(key: key); + const VideoListAutoPlayPage({super.key}); @override State createState() => _VideoListAutoPlayPageState(); @@ -24,9 +24,8 @@ class _VideoListAutoPlayPageState extends State { return Scaffold( appBar: AppBar(title: const Text("Video Auto Play")), body: ListViewObserver( - child: _buildListView(), sliverListContexts: () { - return [if (_ctx1 != null) _ctx1!]; + return [?_ctx1]; }, onObserveAll: (resultMap) { final model = resultMap[_ctx1]; @@ -38,6 +37,7 @@ class _VideoListAutoPlayPageState extends State { } }, leadingOffset: 200, + child: _buildListView(), ), ); } @@ -59,9 +59,7 @@ class _VideoListAutoPlayPageState extends State { return SizedBox( height: 300, child: _hitIndex == index - ? const VideoWidget( - url: 'https://www.w3schools.com/html/movie.mp4', - ) + ? const VideoWidget(url: 'https://www.w3schools.com/html/movie.mp4') : Container( color: Colors.blue[100], child: const Center(child: Text('placeholder')), @@ -70,9 +68,6 @@ class _VideoListAutoPlayPageState extends State { } Container _buildSeparatorView() { - return Container( - color: Colors.white, - height: 8, - ); + return Container(color: Colors.white, height: 8); } } diff --git a/example/lib/features/scene/video_auto_play_list/widgets/video_widget.dart b/example/lib/features/scene/video_auto_play_list/widgets/video_widget.dart index 2d27632..2191583 100644 --- a/example/lib/features/scene/video_auto_play_list/widgets/video_widget.dart +++ b/example/lib/features/scene/video_auto_play_list/widgets/video_widget.dart @@ -3,16 +3,13 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-05-28 14:08:53 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:video_player/video_player.dart'; class VideoWidget extends StatefulWidget { final String url; - const VideoWidget({ - Key? key, - required this.url, - }) : super(key: key); + const VideoWidget({super.key, required this.url}); @override State createState() => _VideoWidgetState(); diff --git a/example/lib/features/scene/visibility_demo/mixin/visibility_exposure_mixin.dart b/example/lib/features/scene/visibility_demo/mixin/visibility_exposure_mixin.dart index 8285fec..7d4e401 100644 --- a/example/lib/features/scene/visibility_demo/mixin/visibility_exposure_mixin.dart +++ b/example/lib/features/scene/visibility_demo/mixin/visibility_exposure_mixin.dart @@ -11,7 +11,7 @@ mixin VisibilityExposureMixin { Map exposureRecordMap = {}; /// Reset exposure record - resetExposureRecordMap() { + void resetExposureRecordMap() { exposureRecordMap.clear(); } @@ -28,7 +28,7 @@ mixin VisibilityExposureMixin { /// [needExposeCallback] Whether to participate in the callback of exposure /// calculation. /// [toExposeCallback] Callback for exposure conditions met. - handleExposure({ + void handleExposure({ required dynamic resultModel, double toExposeDisplayPercent = 0.5, dynamic Function(int index)? recordKeyCallback, diff --git a/example/lib/features/scene/visibility_demo/page/visibility_listview_page.dart b/example/lib/features/scene/visibility_demo/page/visibility_listview_page.dart index 75ce6bf..7f84867 100644 --- a/example/lib/features/scene/visibility_demo/page/visibility_listview_page.dart +++ b/example/lib/features/scene/visibility_demo/page/visibility_listview_page.dart @@ -4,12 +4,12 @@ * @Date: 2023-08-25 23:14:20 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/scene/visibility_demo/mixin/visibility_exposure_mixin.dart'; class VisibilityListViewPage extends StatefulWidget { - const VisibilityListViewPage({Key? key}) : super(key: key); + const VisibilityListViewPage({super.key}); @override State createState() => _VisibilityListViewPageState(); @@ -26,7 +26,6 @@ class _VisibilityListViewPageState extends State return Scaffold( appBar: AppBar(title: const Text("Visibility ListView")), body: ListViewObserver( - child: _buildListView(), triggerOnObserveType: ObserverTriggerOnObserveType.directly, controller: observerController, onObserve: (resultModel) { @@ -49,6 +48,7 @@ class _VisibilityListViewPageState extends State }, ); }, + child: _buildListView(), ), floatingActionButton: FloatingActionButton( onPressed: () { @@ -79,23 +79,18 @@ class _VisibilityListViewPageState extends State color: needExpose ? Colors.red : isEven - ? Colors.orange[300] - : Colors.black12, + ? Colors.orange[300] + : Colors.black12, child: Center( child: Text( "index -- $index", - style: TextStyle( - color: needExpose ? Colors.white : Colors.black, - ), + style: TextStyle(color: needExpose ? Colors.white : Colors.black), ), ), ); } Container _buildSeparatorView() { - return Container( - color: Colors.white, - height: 5, - ); + return Container(color: Colors.white, height: 5); } } diff --git a/example/lib/features/scene/visibility_demo/page/visibility_scrollview_page.dart b/example/lib/features/scene/visibility_demo/page/visibility_scrollview_page.dart index f802fe9..0ddaa6d 100644 --- a/example/lib/features/scene/visibility_demo/page/visibility_scrollview_page.dart +++ b/example/lib/features/scene/visibility_demo/page/visibility_scrollview_page.dart @@ -4,13 +4,13 @@ * @Date: 2023-08-25 23:14:20 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/scene/visibility_demo/mixin/visibility_exposure_mixin.dart'; import 'package:visibility_detector/visibility_detector.dart'; class VisibilityScrollViewPage extends StatefulWidget { - const VisibilityScrollViewPage({Key? key}) : super(key: key); + const VisibilityScrollViewPage({super.key}); @override State createState() => @@ -31,12 +31,8 @@ class _VisibilityScrollViewPageState extends State return Scaffold( body: SliverViewObserver( controller: observerController, - child: _buildScrollView(), sliverContexts: () { - return [ - if (_sliverListCtx != null) _sliverListCtx!, - if (_sliverGridCtx != null) _sliverGridCtx!, - ]; + return [?_sliverListCtx, ?_sliverGridCtx]; }, // autoTriggerObserveTypes: const [ // ObserverAutoTriggerObserveType.scrollEnd, @@ -79,6 +75,7 @@ class _VisibilityScrollViewPageState extends State ); } }, + child: _buildScrollView(), ), ); } @@ -109,30 +106,25 @@ class _VisibilityScrollViewPageState extends State Widget _buildSliverListView() { return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - _sliverListCtx ??= ctx; - final isEven = index % 2 == 0; - final needExpose = index == needExposeIndex; - return Container( - height: isEven ? 200 : 100, - color: needExpose - ? Colors.purple - : isEven - ? Colors.red - : Colors.black12, - child: Center( - child: Text( - "index -- $index", - style: TextStyle( - color: isEven ? Colors.white : Colors.black, - ), - ), + delegate: SliverChildBuilderDelegate((ctx, index) { + _sliverListCtx ??= ctx; + final isEven = index % 2 == 0; + final needExpose = index == needExposeIndex; + return Container( + height: isEven ? 200 : 100, + color: needExpose + ? Colors.purple + : isEven + ? Colors.red + : Colors.black12, + child: Center( + child: Text( + "index -- $index", + style: TextStyle(color: isEven ? Colors.white : Colors.black), ), - ); - }, - childCount: 10, - ), + ), + ); + }, childCount: 10), ); } @@ -143,9 +135,7 @@ class _VisibilityScrollViewPageState extends State child: Container( height: 200, color: Colors.blue, - child: const Center( - child: Text('Middle Sliver'), - ), + child: const Center(child: Text('Middle Sliver')), ), ), onVisibilityChanged: (info) { @@ -164,26 +154,21 @@ class _VisibilityScrollViewPageState extends State crossAxisSpacing: 10.0, childAspectRatio: 2.0, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - _sliverGridCtx ??= context; + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + _sliverGridCtx ??= context; - final needExpose = index == needExposeIndex; + final needExpose = index == needExposeIndex; - return Container( - color: needExpose ? Colors.purple : Colors.green, - child: Center( - child: Text( - 'index -- $index', - style: TextStyle( - color: needExpose ? Colors.white : Colors.black, - ), - ), + return Container( + color: needExpose ? Colors.purple : Colors.green, + child: Center( + child: Text( + 'index -- $index', + style: TextStyle(color: needExpose ? Colors.white : Colors.black), ), - ); - }, - childCount: 20, - ), + ), + ); + }, childCount: 20), ); } } diff --git a/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_grid_item_view.dart b/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_grid_item_view.dart index ac8b39d..d6dec51 100644 --- a/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_grid_item_view.dart +++ b/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_grid_item_view.dart @@ -3,7 +3,7 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2023-06-08 21:59:07 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/video_auto_play_list/widgets/video_widget.dart'; import 'package:scrollview_observer_example/features/scene/waterfall_flow_demo/waterfall_flow_type.dart'; @@ -17,12 +17,12 @@ class WaterfallFlowGridItemView extends StatelessWidget { bool get isHit => selfType == hitType && selfIndex == hitIndex; const WaterfallFlowGridItemView({ - Key? key, + super.key, required this.selfIndex, required this.selfType, required this.hitIndex, required this.hitType, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -41,9 +41,7 @@ class WaterfallFlowGridItemView extends StatelessWidget { isHit ? _buildVideo() : _buildCover(), const SizedBox(height: 10), Text('grid item $selfIndex'), - SizedBox( - height: 50.0 + 50.0 * (selfIndex % 2), - ), + SizedBox(height: 50.0 + 50.0 * (selfIndex % 2)), ], ); } @@ -65,7 +63,7 @@ class WaterfallFlowGridItemView extends StatelessWidget { child: CircularProgressIndicator( value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! + loadingProgress.expectedTotalBytes! : null, ), ), diff --git a/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_page.dart b/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_page.dart index 215fcef..4b367f4 100644 --- a/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_page.dart +++ b/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_page.dart @@ -3,7 +3,7 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2023-05-14 16:22:36 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/features/scene/waterfall_flow_demo/waterfall_flow_grid_item_view.dart'; import 'package:scrollview_observer_example/features/scene/waterfall_flow_demo/waterfall_flow_swipe_view.dart'; @@ -11,7 +11,7 @@ import 'package:scrollview_observer_example/features/scene/waterfall_flow_demo/w import 'package:waterfall_flow/waterfall_flow.dart'; class WaterfallFlowPage extends StatefulWidget { - const WaterfallFlowPage({Key? key}) : super(key: key); + const WaterfallFlowPage({super.key}); @override State createState() => WaterfallFlowPageState(); @@ -35,7 +35,6 @@ class WaterfallFlowPageState extends State { return Scaffold( appBar: AppBar(title: const Text('Waterfall Flow')), body: SliverViewObserver( - child: _buildBody(), leadingOffset: observeOffset, scrollNotificationPredicate: defaultScrollNotificationPredicate, autoTriggerObserveTypes: const [ @@ -44,8 +43,8 @@ class WaterfallFlowPageState extends State { triggerOnObserveType: ObserverTriggerOnObserveType.directly, extendedHandleObserve: (context) { // An extension of the original observation logic. - final _obj = ObserverUtils.findRenderObject(context); - if (_obj is RenderSliverWaterfallFlow) { + final obj = ObserverUtils.findRenderObject(context); + if (obj is RenderSliverWaterfallFlow) { return ObserverCore.handleGridObserve( context: context, fetchLeadingOffset: () => observeOffset, @@ -65,11 +64,7 @@ class WaterfallFlowPageState extends State { // return null; // }, sliverContexts: () { - return [ - if (grid1Context != null) grid1Context!, - if (swipeContext != null) swipeContext!, - if (grid2Context != null) grid2Context!, - ]; + return [?grid1Context, ?swipeContext, ?grid2Context]; }, onObserveViewport: (result) { firstChildCtxInViewport = result.firstChild.sliverContext; @@ -110,6 +105,7 @@ class WaterfallFlowPageState extends State { handleGridHitIndex(firstIndexList); } }, + child: _buildBody(), ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.swipe), @@ -122,7 +118,7 @@ class WaterfallFlowPageState extends State { ); } - handleGridHitIndex(List firstIndexList) { + void handleGridHitIndex(List firstIndexList) { if (firstIndexList.isEmpty) return; // debugPrint('gridContext displaying -- $firstIndexList'); int targetIndex = firstIndexList.indexOf(hitIndex); @@ -184,35 +180,29 @@ class WaterfallFlowPageState extends State { ); } - Widget _buildGridView({ - bool isFirst = false, - required int childCount, - }) { + Widget _buildGridView({bool isFirst = false, required int childCount}) { return SliverWaterfallFlow( gridDelegate: const SliverWaterfallFlowDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 15, crossAxisSpacing: 10, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - WaterFlowHitType selfType; - if (isFirst) { - if (grid1Context != context) grid1Context = context; - selfType = WaterFlowHitType.firstGrid; - } else { - if (grid2Context != context) grid2Context = context; - selfType = WaterFlowHitType.secondGrid; - } - return WaterfallFlowGridItemView( - selfIndex: index, - selfType: selfType, - hitIndex: hitIndex, - hitType: hitType, - ); - }, - childCount: childCount, - ), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + WaterFlowHitType selfType; + if (isFirst) { + if (grid1Context != context) grid1Context = context; + selfType = WaterFlowHitType.firstGrid; + } else { + if (grid2Context != context) grid2Context = context; + selfType = WaterFlowHitType.secondGrid; + } + return WaterfallFlowGridItemView( + selfIndex: index, + selfType: selfType, + hitIndex: hitIndex, + hitType: hitType, + ); + }, childCount: childCount), ); } @@ -230,8 +220,6 @@ class WaterfallFlowPageState extends State { } Widget _buildSeparator(double size) { - return SliverToBoxAdapter( - child: Container(height: size), - ); + return SliverToBoxAdapter(child: Container(height: size)); } } diff --git a/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_swipe_view.dart b/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_swipe_view.dart index f0cdaa3..e98ec1e 100644 --- a/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_swipe_view.dart +++ b/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_swipe_view.dart @@ -3,17 +3,14 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2023-06-08 22:03:17 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/features/scene/video_auto_play_list/widgets/video_widget.dart'; import 'package:scrollview_observer_example/features/scene/waterfall_flow_demo/waterfall_flow_type.dart'; class WaterfallFlowSwipeView extends StatefulWidget { final WaterFlowHitType hitType; - const WaterfallFlowSwipeView({ - Key? key, - required this.hitType, - }) : super(key: key); + const WaterfallFlowSwipeView({super.key, required this.hitType}); @override State createState() => _WaterfallFlowSwipeViewState(); diff --git a/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_type.dart b/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_type.dart index 90a5456..0c86a2a 100644 --- a/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_type.dart +++ b/example/lib/features/scene/waterfall_flow_demo/waterfall_flow_type.dart @@ -3,8 +3,4 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2023-06-06 22:16:27 */ -enum WaterFlowHitType { - firstGrid, - swipe, - secondGrid, -} +enum WaterFlowHitType { firstGrid, swipe, secondGrid } diff --git a/example/lib/features/scene/waterfall_flow_fixed_height_demo/waterfall_flow_fixed_height_page.dart b/example/lib/features/scene/waterfall_flow_fixed_height_demo/waterfall_flow_fixed_height_page.dart index 6340ed3..4287bf5 100644 --- a/example/lib/features/scene/waterfall_flow_fixed_height_demo/waterfall_flow_fixed_height_page.dart +++ b/example/lib/features/scene/waterfall_flow_fixed_height_demo/waterfall_flow_fixed_height_page.dart @@ -4,13 +4,13 @@ * @Date: 2023-10-25 21:29:37 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer_example/utils/snackbar.dart'; import 'package:waterfall_flow/waterfall_flow.dart'; class WaterfallFlowFixedHeightPage extends StatefulWidget { - const WaterfallFlowFixedHeightPage({Key? key}) : super(key: key); + const WaterfallFlowFixedHeightPage({super.key}); @override State createState() => @@ -45,7 +45,6 @@ class WaterfallFlowFixedHeightPageState return Scaffold( appBar: AppBar(title: const Text('Waterfall Flow')), body: SliverViewObserver( - child: _buildBody(), controller: observerController, autoTriggerObserveTypes: const [ ObserverAutoTriggerObserveType.scrollEnd, @@ -63,27 +62,24 @@ class WaterfallFlowFixedHeightPageState // return null; // }, sliverContexts: () { - return [ - if (grid1Context != null) grid1Context!, - ]; + return [?grid1Context]; }, onObserveViewport: (result) { firstChildCtxInViewport = result.firstChild.sliverContext; debugPrint( - 'current first sliver in viewport - $firstChildCtxInViewport'); + 'current first sliver in viewport - $firstChildCtxInViewport', + ); }, onObserveAll: (resultMap) { final result = resultMap[firstChildCtxInViewport]; debugPrint('all Observe for current first sliver - $result'); }, + child: _buildBody(), ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.airline_stops_outlined), onPressed: () { - SnackBarUtil.showSnackBar( - context: context, - text: 'Jump to item 13', - ); + SnackBarUtil.showSnackBar(context: context, text: 'Jump to item 13'); observerController.jumpTo( index: 13, isFixedHeight: true, @@ -97,36 +93,29 @@ class WaterfallFlowFixedHeightPageState Widget _buildBody() { return CustomScrollView( controller: scrollController, - slivers: [ - _buildGridView(childCount: 50), - ], + slivers: [_buildGridView(childCount: 50)], ); } - Widget _buildGridView({ - required int childCount, - }) { + Widget _buildGridView({required int childCount}) { return SliverWaterfallFlow( gridDelegate: const SliverWaterfallFlowDelegateWithFixedCrossAxisCount( crossAxisCount: 3, mainAxisSpacing: 15, crossAxisSpacing: 10, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - if (grid1Context != context) grid1Context = context; - return Container( - height: 50, - color: Colors.primaries[index % Colors.primaries.length], - alignment: Alignment.topLeft, - child: Text( - 'item: $index', - style: const TextStyle(fontSize: 20, color: Colors.white), - ), - ); - }, - childCount: childCount, - ), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + if (grid1Context != context) grid1Context = context; + return Container( + height: 50, + color: Colors.primaries[index % Colors.primaries.length], + alignment: Alignment.topLeft, + child: Text( + 'item: $index', + style: const TextStyle(fontSize: 20, color: Colors.white), + ), + ); + }, childCount: childCount), ); } } diff --git a/example/lib/main.dart b/example/lib/main.dart index 719213c..86fb903 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,7 +4,7 @@ * @Date: 2022-05-28 12:32:34 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer_example/common/route/route.dart'; void main() { @@ -12,12 +12,10 @@ void main() { } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); + const MyApp({super.key}); @override Widget build(BuildContext context) { - return MaterialApp.router( - routerConfig: MyRoute.routerConfig, - ); + return MaterialApp.router(routerConfig: MyRoute.routerConfig); } } diff --git a/example/lib/utils/keyboard.dart b/example/lib/utils/keyboard.dart index cc092b7..e737a20 100644 --- a/example/lib/utils/keyboard.dart +++ b/example/lib/utils/keyboard.dart @@ -4,7 +4,7 @@ * @Date: 2023-11-08 21:48:11 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; class KeyboardTool { static void dismissKeyboard(BuildContext context) { diff --git a/example/lib/utils/snackbar.dart b/example/lib/utils/snackbar.dart index 4a4dc0c..0d1f554 100644 --- a/example/lib/utils/snackbar.dart +++ b/example/lib/utils/snackbar.dart @@ -4,10 +4,10 @@ * @Date: 2023-10-29 12:44:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; class SnackBarUtil { - static showSnackBar({ + static void showSnackBar({ required BuildContext context, required String text, }) { diff --git a/example/lib/widgets/animation.dart b/example/lib/widgets/animation.dart index e1c37eb..f1f1d1a 100644 --- a/example/lib/widgets/animation.dart +++ b/example/lib/widgets/animation.dart @@ -4,7 +4,7 @@ * @Date: 2024-05-29 22:22:07 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; class SlideAnimation extends StatelessWidget { final Curve curve; @@ -18,15 +18,14 @@ class SlideAnimation extends StatelessWidget { final AnimationController controller; const SlideAnimation({ - Key? key, + super.key, this.curve = Curves.ease, required this.controller, double? verticalOffset, double? horizontalOffset, required this.child, - }) : verticalOffset = verticalOffset ?? 0.0, - horizontalOffset = horizontalOffset ?? 0.0, - super(key: key); + }) : verticalOffset = verticalOffset ?? 0.0, + horizontalOffset = horizontalOffset ?? 0.0; @override Widget build(BuildContext context) { @@ -72,11 +71,11 @@ class FadeInAnimation extends StatelessWidget { final AnimationController controller; const FadeInAnimation({ - Key? key, + super.key, this.curve = Curves.ease, required this.controller, required this.child, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -95,10 +94,7 @@ class FadeInAnimation extends StatelessWidget { ), ); - return Opacity( - opacity: opacityAnimation.value, - child: child, - ); + return Opacity(opacity: opacityAnimation.value, child: child); } } @@ -112,10 +108,10 @@ class AnimationExecutor extends StatefulWidget { final AnimationController controller; const AnimationExecutor({ - Key? key, + super.key, required this.builder, required this.controller, - }) : super(key: key); + }); @override State createState() => _AnimationExecutorState(); diff --git a/example/lib/widgets/sliver.dart b/example/lib/widgets/sliver.dart index b503423..627f2aa 100644 --- a/example/lib/widgets/sliver.dart +++ b/example/lib/widgets/sliver.dart @@ -4,7 +4,7 @@ * @Date: 2024-05-29 22:20:12 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; typedef SliverHeaderBuilder = Widget Function( BuildContext context, @@ -17,15 +17,15 @@ class SliverHeaderDelegate extends SliverPersistentHeaderDelegate { required this.maxHeight, this.minHeight = 0, required Widget child, - }) : builder = ((a, b, c) => child), - assert(minHeight <= maxHeight && minHeight >= 0); + }) : builder = ((a, b, c) => child), + assert(minHeight <= maxHeight && minHeight >= 0); SliverHeaderDelegate.fixedHeight({ required double height, required Widget child, - }) : builder = ((a, b, c) => child), - maxHeight = height, - minHeight = height; + }) : builder = ((a, b, c) => child), + maxHeight = height, + minHeight = height; SliverHeaderDelegate.builder({ required this.maxHeight, diff --git a/example/pubspec.lock b/example/pubspec.lock index 1013ca3..cdbebe2 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -42,7 +42,7 @@ packages: source: hosted version: "1.1.2" collection: - dependency: transitive + dependency: "direct main" description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" @@ -61,10 +61,18 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" + url: "https://pub.dev" + source: hosted + version: "1.0.9" + cupertino_ui: + dependency: "direct main" + description: + name: cupertino_ui + sha256: e9dfe7fac704028f8928cbe4028a0be5e8a709498e8daf8247de99e99a32aef3 url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "1.0.2" easy_refresh: dependency: "direct main" description: @@ -74,7 +82,7 @@ packages: source: hosted version: "3.4.0" extended_list: - dependency: transitive + dependency: "direct main" description: name: extended_list sha256: fa7bcb2645b7d6849918d499fda6ea917cda85e43b2e06dfec2a29b649722974 @@ -106,10 +114,15 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493 + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "6.0.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_sticky_header: dependency: "direct main" description: @@ -161,6 +174,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.15.6" + intl: + dependency: transitive + description: + name: intl + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" + url: "https://pub.dev" + source: hosted + version: "0.20.3" leak_tracker: dependency: transitive description: @@ -189,10 +210,10 @@ packages: dependency: transitive description: name: lints - sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "6.1.0" loading_more_list: dependency: "direct main" description: @@ -221,10 +242,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.20" material_color_utilities: dependency: transitive description: @@ -233,14 +254,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.13.0" + material_ui: + dependency: "direct main" + description: + name: material_ui + sha256: fbfb53cab6c4629438feeade5f3d305f72944bc9d9f25786b19106d32b80ec45 + url: "https://pub.dev" + source: hosted + version: "1.2.0" meta: dependency: transitive description: name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" path: dependency: transitive description: @@ -279,7 +308,7 @@ packages: path: ".." relative: true source: path - version: "1.27.0" + version: "2.0.0" sky_engine: dependency: transitive description: flutter @@ -329,10 +358,10 @@ packages: dependency: transitive description: name: test_api - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.12" value_layout_builder: dependency: transitive description: @@ -345,10 +374,10 @@ packages: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.2" video_player: dependency: "direct main" description: @@ -422,5 +451,5 @@ packages: source: hosted version: "1.1.1" sdks: - dart: ">=3.10.0-0 <4.0.0" - flutter: ">=3.32.0" + dart: ">=3.13.0 <4.0.0" + flutter: ">=3.47.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index b23a800..5d72f1a 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,38 +1,16 @@ name: scrollview_observer_example description: A new Flutter project. - -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: "none" # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +publish_to: "none" version: 1.0.0+1 environment: - sdk: ">=2.19.0 <4.0.0" + sdk: ^3.13.0 -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 + cupertino_icons: ^1.0.9 video_player: ^2.4.2 waterfall_flow: ^3.0.2 loading_more_list: ^5.0.3 @@ -47,6 +25,10 @@ dependencies: scrollview_observer: path: ../ + material_ui: ^1.2.0 + extended_list: ^3.0.2 + cupertino_ui: ^1.0.2 + collection: ^1.19.1 dependency_overrides: get: git: @@ -62,7 +44,7 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^1.0.0 + flutter_lints: ^6.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index 4e36bc2..e3ecfc1 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -10,26 +10,17 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:scrollview_observer_example/main.dart'; void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. + testWidgets('Home page smoke test', (WidgetTester tester) async { await tester.pumpWidget(const MyApp()); + await tester.pumpAndSettle(); - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); + expect(find.byType(MaterialApp), findsOneWidget); + expect(find.byType(Scaffold), findsWidgets); }); } diff --git a/lib/scrollview_observer.dart b/lib/scrollview_observer.dart index 6c70e23..e7d4903 100644 --- a/lib/scrollview_observer.dart +++ b/lib/scrollview_observer.dart @@ -4,7 +4,7 @@ * @Date: 2022-08-08 00:20:03 */ -library scrollview_observer; +library; export 'src/notification.dart'; diff --git a/lib/src/common/models/observe_displaying_child_model_mixin.dart b/lib/src/common/models/observe_displaying_child_model_mixin.dart index b56441b..5fb78e7 100644 --- a/lib/src/common/models/observe_displaying_child_model_mixin.dart +++ b/lib/src/common/models/observe_displaying_child_model_mixin.dart @@ -62,25 +62,25 @@ mixin ObserveDisplayingChildModelMixin on ObserveDisplayingChildModel { /// The visible size of the child widget in the main axis. double get visibleMainAxisSize { if (paintExtent == 0) return 0; - double _visibleMainAxisSize = mainAxisSize; + double visibleMainAxisSize = mainAxisSize; final currentChildLayoutOffset = layoutOffset; final scrollOffset = sliver.constraints.scrollOffset; final leadingScrollViewOffset = scrollOffset + overlap; if (leadingScrollViewOffset > currentChildLayoutOffset) { // The child widget is blocked by the leading direction side of viewport. - _visibleMainAxisSize = + visibleMainAxisSize = mainAxisSize - (leadingScrollViewOffset - currentChildLayoutOffset); } else if (scrollOffset + paintExtent > currentChildLayoutOffset + mainAxisSize) { // The child widget is being fully displayed. - _visibleMainAxisSize = mainAxisSize; + visibleMainAxisSize = mainAxisSize; } else { // The child widget is blocked by the trailing direction side of viewport. - _visibleMainAxisSize = + visibleMainAxisSize = scrollOffset + paintExtent - currentChildLayoutOffset; } - _visibleMainAxisSize = _visibleMainAxisSize.clamp(0, mainAxisSize); - return _visibleMainAxisSize; + visibleMainAxisSize = visibleMainAxisSize.clamp(0, mainAxisSize); + return visibleMainAxisSize; } /// The visible fraction of the child widget on the corresponding sliver. diff --git a/lib/src/common/models/observe_scroll_child_model.dart b/lib/src/common/models/observe_scroll_child_model.dart index 6ac4975..7638e4c 100644 --- a/lib/src/common/models/observe_scroll_child_model.dart +++ b/lib/src/common/models/observe_scroll_child_model.dart @@ -10,8 +10,5 @@ class ObserveScrollChildModel { /// The layout offset of child widget. double layoutOffset; - ObserveScrollChildModel({ - required this.size, - required this.layoutOffset, - }); + ObserveScrollChildModel({required this.size, required this.layoutOffset}); } diff --git a/lib/src/common/models/observer_handle_contexts_result_model.dart b/lib/src/common/models/observer_handle_contexts_result_model.dart index 8dbda8d..6f854ea 100644 --- a/lib/src/common/models/observer_handle_contexts_result_model.dart +++ b/lib/src/common/models/observer_handle_contexts_result_model.dart @@ -4,7 +4,7 @@ * @Date: 2023-08-12 16:01:29 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/src/common/models/observe_model.dart'; class ObserverHandleContextsResultModel { diff --git a/lib/src/common/models/observer_index_position_model.dart b/lib/src/common/models/observer_index_position_model.dart index c16fd8c..629f2de 100644 --- a/lib/src/common/models/observer_index_position_model.dart +++ b/lib/src/common/models/observer_index_position_model.dart @@ -1,4 +1,4 @@ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/src/common/typedefs.dart'; class ObserverIndexPositionModel { diff --git a/lib/src/common/observer_controller.dart b/lib/src/common/observer_controller.dart index c367820..c81cabd 100644 --- a/lib/src/common/observer_controller.dart +++ b/lib/src/common/observer_controller.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer/src/common/models/observe_find_child_model.dart'; @@ -55,11 +55,11 @@ class ObserverController { /// Get the target sliver [BuildContext] BuildContext? fetchSliverContext({BuildContext? sliverContext}) { - BuildContext? _sliverContext = sliverContext; - if (_sliverContext == null && sliverContexts.isNotEmpty) { - _sliverContext = sliverContexts.first; + BuildContext? sliverContext0 = sliverContext; + if (sliverContext0 == null && sliverContexts.isNotEmpty) { + sliverContext0 = sliverContexts.first; } - return _sliverContext; + return sliverContext0; } /// Get the latest target sliver [BuildContext] and reset some of the old data. @@ -72,9 +72,11 @@ class ObserverController { } mixin ObserverControllerForNotification< - M extends ObserveModel, - R extends ObserverHandleContextsResultModel, - S extends CommonOnceObserveNotificationResult> on ObserverController { + M extends ObserveModel, + R extends ObserverHandleContextsResultModel, + S extends CommonOnceObserveNotificationResult +> + on ObserverController { /// A completer for dispatch once observation Completer? innerDispatchOnceObserveCompleter; @@ -85,17 +87,15 @@ mixin ObserverControllerForNotification< }) { Completer completer = Completer(); innerDispatchOnceObserveCompleter = completer; - BuildContext? _sliverContext = fetchSliverContext( + BuildContext? sliverContext0 = fetchSliverContext( sliverContext: sliverContext, ); - notification.dispatch(_sliverContext); + notification.dispatch(sliverContext0); return completer.future; } /// Complete the observation notification - void innerHandleDispatchOnceObserveComplete({ - required R? resultModel, - }) { + void innerHandleDispatchOnceObserveComplete({required R? resultModel}) { final completer = innerDispatchOnceObserveCompleter; if (completer == null) return; if (!completer.isCompleted) { @@ -163,7 +163,8 @@ mixin ObserverControllerForInfo on ObserverController { /// Find out the current last child in sliver RenderIndexedSemantics? findCurrentLastChild( - RenderSliverMultiBoxAdaptor obj) { + RenderSliverMultiBoxAdaptor obj, + ) { RenderIndexedSemantics? child; final lastChild = obj.lastChild; if (lastChild == null) return null; @@ -279,7 +280,8 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { /// Clear the offset cache that jumping to a specified index location. @Deprecated( - 'It will be removed in version 2, please use [clearScrollIndexCache] instead') + 'It will be removed in version 2, please use [clearScrollIndexCache] instead', + ) void clearIndexOffsetCache(BuildContext? sliverContext) { clearScrollIndexCache(sliverContext: sliverContext); } @@ -402,17 +404,19 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { required ObserverRenderSliverType? renderSliverType, required ObserverOnPrepareScrollToIndex? onPrepareScrollToIndex, }) async { - assert(alignment.clamp(0, 1) == alignment, - 'The [alignment] is expected to be a value in the range [0.0, 1.0]'); - assert(controller != null); - var _controller = controller; + assert( + alignment.clamp(0, 1) == alignment, + 'The [alignment] is expected to be a value in the range [0.0, 1.0]', + ); + assert(this.controller != null); + final controller = this.controller; final ctx = fetchSliverContext(sliverContext: sliverContext); if (ctx == null) { _handleScrollInterruption(context: ctx, completer: completer); return; } - if (_controller == null || !_controller.hasClients) { + if (controller == null || !controller.hasClients) { _handleScrollInterruption(context: ctx, completer: completer); return; } @@ -453,14 +457,14 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { double paintScrollExtent = precedingScrollExtent + (obj.geometry?.maxPaintExtent ?? 0); double targetScrollExtent = precedingScrollExtent; - final pixels = _controller.position.pixels.rectify(obj); + final pixels = controller.position.pixels.rectify(obj); if (pixels > paintScrollExtent) { targetScrollExtent = paintScrollExtent; } if (targetScrollExtent > maxScrollExtent) { targetScrollExtent = maxScrollExtent; } - await _controller.animateTo( + await controller.animateTo( targetScrollExtent.rectify(obj), duration: _findingDuration, curve: _findingCurve, @@ -470,15 +474,16 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { final precedingScrollExtent = obj.constraints.precedingScrollExtent; final viewportOffset = viewport.offset.pixels.rectify(obj); final isHorizontal = obj.constraints.axis == Axis.horizontal; - final viewportSize = - isHorizontal ? viewport.size.width : viewport.size.height; + final viewportSize = isHorizontal + ? viewport.size.width + : viewport.size.height; final viewportBoundaryExtent = // ignore: deprecated_member_use viewportSize * 0.5 + (viewport.cacheExtent ?? 0); if (precedingScrollExtent > (viewportOffset + viewportBoundaryExtent)) { double targetOffset = precedingScrollExtent - viewportBoundaryExtent; if (targetOffset > maxScrollExtent) targetOffset = maxScrollExtent; - await _controller.animateTo( + await controller.animateTo( targetOffset.rectify(obj), duration: _findingDuration, curve: _findingCurve, @@ -488,6 +493,12 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { } } + // The sliver may have been unmounted while scrolling around. + if (!ctx.mounted) { + _handleScrollInterruption(context: null, completer: completer); + return; + } + var targetScrollChildModel = indexOffsetMap[ctx]?[index]; // There is a cache offset, scroll to the offset directly. if (targetScrollChildModel != null) { @@ -504,7 +515,7 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { isAnimateTo: isAnimateTo, duration: duration, curve: curve, - controller: _controller, + controller: controller, obj: obj, calcResult: calcResult, childSize: targetScrollChildModel.size, @@ -514,7 +525,7 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { onPrepareScrollToIndex: onPrepareScrollToIndex, ); - _handleScrollEnd(context: ctx, completer: completer); + _handleScrollEnd(context: ctx.mounted ? ctx : null, completer: completer); return; } @@ -580,9 +591,9 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { required ObserverRenderSliverType? renderSliverType, required ObserverOnPrepareScrollToIndex? onPrepareScrollToIndex, }) async { - assert(controller != null); - var _controller = controller; - if (_controller == null || !_controller.hasClients) { + assert(this.controller != null); + final controller = this.controller; + if (controller == null || !controller.hasClients) { _handleScrollInterruption(context: ctx, completer: completer); return; } @@ -641,7 +652,7 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { isAnimateTo: isAnimateTo, duration: isAnimateTo ? duration : null, curve: isAnimateTo ? curve : null, - controller: _controller, + controller: controller, obj: obj, calcResult: calcResult, childSize: childMainAxisSize, @@ -650,7 +661,7 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { offset: offset, onPrepareScrollToIndex: onPrepareScrollToIndex, ); - _handleScrollEnd(context: ctx, completer: completer); + _handleScrollEnd(context: ctx.mounted ? ctx : null, completer: completer); } /// Scrolling to the specified index location by gradually scrolling around @@ -670,8 +681,8 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { double? lastPageTurningOffset, ObserverOnPrepareScrollToIndex? onPrepareScrollToIndex, }) async { - var _controller = controller; - if (_controller == null || !_controller.hasClients) { + final controller = this.controller; + if (controller == null || !controller.hasClients) { _handleScrollInterruption(context: ctx, completer: completer); return; } @@ -692,8 +703,9 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { final precedingScrollExtent = obj.constraints.precedingScrollExtent; if (index < firstChildIndex) { - final sliverSize = - isHorizontal ? obj.paintBounds.width : obj.paintBounds.height; + final sliverSize = isHorizontal + ? obj.paintBounds.width + : obj.paintBounds.height; double childLayoutOffset = 0; final firstChild = findCurrentFirstChild(obj); final parentData = firstChild?.parentData; @@ -709,21 +721,23 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { // The offset of this page turning is the same as the previous one, // which means the [index] is wrong. if (lastPageTurningOffset == prevPageOffset) { - Log.warning('The child corresponding to the index cannot be found.\n' - 'Please make sure the index is correct.'); + Log.warning( + 'The child corresponding to the index cannot be found.\n' + 'Please make sure the index is correct.', + ); _handleScrollInterruption(context: ctx, completer: completer); return; } lastPageTurningOffset = prevPageOffset; final prevPageOffsetRectified = prevPageOffset.rectify(obj); if (isAnimateTo) { - await _controller.animateTo( + await controller.animateTo( prevPageOffsetRectified, duration: _findingDuration, curve: _findingCurve, ); } else { - _controller.jumpTo(prevPageOffsetRectified); + controller.jumpTo(prevPageOffsetRectified); } ambiguate(WidgetsBinding.instance)?.addPostFrameCallback((_) { @@ -753,7 +767,8 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { }); } else if (index > lastChildIndex) { final lastChild = findCurrentLastChild(obj); - final childSize = (isHorizontal + final childSize = + (isHorizontal ? lastChild?.paintBounds.width : lastChild?.paintBounds.height) ?? 0; @@ -764,26 +779,29 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { } double nextPageOffset = childLayoutOffset + childSize + precedingScrollExtent; - nextPageOffset = - nextPageOffset > maxScrollExtent ? maxScrollExtent : nextPageOffset; + nextPageOffset = nextPageOffset > maxScrollExtent + ? maxScrollExtent + : nextPageOffset; // The offset of this page turning is the same as the previous one, // which means the [index] is wrong. if (lastPageTurningOffset == nextPageOffset) { - Log.warning('The child corresponding to the index cannot be found.\n' - 'Please make sure the index is correct.'); + Log.warning( + 'The child corresponding to the index cannot be found.\n' + 'Please make sure the index is correct.', + ); _handleScrollInterruption(context: ctx, completer: completer); return; } lastPageTurningOffset = nextPageOffset; final nextPageOffsetRectified = nextPageOffset.rectify(obj); if (isAnimateTo) { - await _controller.animateTo( + await controller.animateTo( nextPageOffsetRectified, duration: _findingDuration, curve: _findingCurve, ); } else { - _controller.jumpTo(nextPageOffsetRectified); + controller.jumpTo(nextPageOffsetRectified); } ambiguate(WidgetsBinding.instance)?.addPostFrameCallback((_) { @@ -815,6 +833,10 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { // Target index child is already in viewport var targetChild = obj.firstChild; while (targetChild != null) { + if (!ctx.mounted) { + _handleScrollInterruption(context: null, completer: completer); + return; + } if (targetChild is! RenderIndexedSemantics) { targetChild = obj.childAfter(targetChild); continue; @@ -827,8 +849,9 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { } final isHorizontal = obj.constraints.axis == Axis.horizontal; final childPaintBounds = targetChild.paintBounds; - final childSize = - isHorizontal ? childPaintBounds.width : childPaintBounds.height; + final childSize = isHorizontal + ? childPaintBounds.width + : childPaintBounds.height; _updateIndexOffsetMap( ctx: ctx, index: currentChildIndex, @@ -853,12 +876,15 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { isAnimateTo: isAnimateTo, duration: isAnimateTo ? duration : null, curve: isAnimateTo ? curve : null, - controller: _controller, + controller: controller, calcResult: calcResult, onPrepareScrollToIndex: onPrepareScrollToIndex, ); - _handleScrollEnd(context: ctx, completer: completer); + _handleScrollEnd( + context: ctx.mounted ? ctx : null, + completer: completer, + ); } break; } @@ -1000,7 +1026,8 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { ); final maxScrollExtent = extremeScrollExtent.rectify(obj); remainingBottomExtent = maxScrollExtent - scrollOffset; - needScrollExtent = childLayoutOffset + + needScrollExtent = + childLayoutOffset + precedingScrollExtent + targetItemLeadingPadding - scrollOffset; @@ -1014,12 +1041,14 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { // The (estimated) total scrollable extent of this sliver. double scrollExtent = geometry?.scrollExtent ?? 0; scrollOffset = obj.constraints.scrollOffset; - remainingBottomExtent = scrollExtent + + remainingBottomExtent = + scrollExtent + precedingScrollExtent + trailingPadding - scrollOffset - viewportExtent; - needScrollExtent = childLayoutOffset + + needScrollExtent = + childLayoutOffset + precedingScrollExtent + targetItemLeadingPadding - scrollOffset; @@ -1049,7 +1078,7 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { /// Calculate the information about scrolling to the specified index location /// when the type is ObserverRenderSliverType.list. ObserveScrollToIndexFixedHeightResultModel - _calculateScrollToIndexForFixedHeightResultForList({ + _calculateScrollToIndexForFixedHeightResultForList({ required RenderSliverMultiBoxAdaptor obj, required RenderIndexedSemantics targetChild, required int index, @@ -1060,8 +1089,9 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { double itemSeparatorHeight = 0; /// The size of item on the main axis. - final childMainAxisSize = - isHorizontal ? childPaintBounds.width : childPaintBounds.height; + final childMainAxisSize = isHorizontal + ? childPaintBounds.width + : childPaintBounds.height; var nextChild = obj.childAfter(targetChild); nextChild ??= obj.childBefore(targetChild); @@ -1087,7 +1117,7 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { /// Calculate the information about scrolling to the specified index location /// when the type is ObserverRenderSliverType.grid. ObserveScrollToIndexFixedHeightResultModel - _calculateScrollToIndexForFixedHeightResultForGrid({ + _calculateScrollToIndexForFixedHeightResultForGrid({ required RenderSliverMultiBoxAdaptor obj, required RenderIndexedSemantics targetChild, required int index, @@ -1100,8 +1130,9 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { int indexOfLine = index; /// The size of item on the main axis. - final childMainAxisSize = - isHorizontal ? childPaintBounds.width : childPaintBounds.height; + final childMainAxisSize = isHorizontal + ? childPaintBounds.width + : childPaintBounds.height; double crossAxisSpacing = 0; bool isHaveSetCrossAxisSpacing = false; @@ -1118,10 +1149,12 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { // Find the next child on the same line and calculate the // crossAxisSpacing. if (isHorizontal) { - crossAxisSpacing = (nextChildOrigin.dy - targetChildOrigin.dy).abs() - + crossAxisSpacing = + (nextChildOrigin.dy - targetChildOrigin.dy).abs() - childPaintBounds.height; } else { - crossAxisSpacing = (nextChildOrigin.dx - targetChildOrigin.dx).abs() - + crossAxisSpacing = + (nextChildOrigin.dx - targetChildOrigin.dx).abs() - childPaintBounds.width; } isHaveSetCrossAxisSpacing = true; @@ -1133,11 +1166,11 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { if (isHorizontal) { itemSeparatorHeight = (nextChildOrigin.dx - targetChildOrigin.dx).abs() - - childPaintBounds.width; + childPaintBounds.width; } else { itemSeparatorHeight = (nextChildOrigin.dy - targetChildOrigin.dy).abs() - - childPaintBounds.height; + childPaintBounds.height; } } else { // Can't find the next child that is not on the same line. @@ -1153,20 +1186,22 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { if (!isHaveSetCrossAxisSpacing) { // Find two child on the same line and calculate the // crossAxisSpacing. - double firstBeforeCrossAxisOrigin = - isHorizontal ? previousChildOrigin.dy : previousChildOrigin.dx; + double firstBeforeCrossAxisOrigin = isHorizontal + ? previousChildOrigin.dy + : previousChildOrigin.dx; previousChild = obj.childBefore(previousChild); previousChildOrigin = previousChild?.localToGlobal(Offset.zero) ?? Offset.zero; if (previousChild != null) { - double secondBeforeCrossAxisOrigin = - isHorizontal ? previousChildOrigin.dy : previousChildOrigin.dx; + double secondBeforeCrossAxisOrigin = isHorizontal + ? previousChildOrigin.dy + : previousChildOrigin.dx; crossAxisSpacing = (firstBeforeCrossAxisOrigin - secondBeforeCrossAxisOrigin) - .abs() - - (isHorizontal - ? childPaintBounds.height - : childPaintBounds.width); + .abs() - + (isHorizontal + ? childPaintBounds.height + : childPaintBounds.width); isHaveSetCrossAxisSpacing = true; } } else { @@ -1179,21 +1214,23 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { if (isHorizontal) { itemSeparatorHeight = (targetChildOrigin.dx - previousChildOrigin.dx).abs() - - childPaintBounds.width; + childPaintBounds.width; } else { itemSeparatorHeight = (targetChildOrigin.dy - previousChildOrigin.dy).abs() - - childPaintBounds.height; + childPaintBounds.height; } } } - final childCrossAxisSize = - isHorizontal ? childPaintBounds.height : childPaintBounds.width; + final childCrossAxisSize = isHorizontal + ? childPaintBounds.height + : childPaintBounds.width; // Calculate the number of lines. // round() for avoiding precision errors. - int itemsPerLine = ((obj.constraints.crossAxisExtent + crossAxisSpacing) / - (childCrossAxisSize + crossAxisSpacing)) - .round(); + int itemsPerLine = + ((obj.constraints.crossAxisExtent + crossAxisSpacing) / + (childCrossAxisSize + crossAxisSpacing)) + .round(); // Calculate the number of lines. indexOfLine = (index / itemsPerLine).floor(); // Calculate the offset of the target child widget on the main axis. @@ -1227,9 +1264,7 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { } /// Called when starting the scrolling task. - void _handleScrollStart({ - required BuildContext? context, - }) { + void _handleScrollStart({required BuildContext? context}) { innerIsHandlingScroll = true; ObserverScrollStartNotification().dispatch(context); } @@ -1248,9 +1283,7 @@ mixin ObserverControllerForScroll on ObserverControllerForInfo { } /// Called when the item with the specified index has been found. - void _handleScrollDecision({ - required BuildContext? context, - }) { + void _handleScrollDecision({required BuildContext? context}) { ObserverScrollDecisionNotification().dispatch(context); } diff --git a/lib/src/common/observer_listener.dart b/lib/src/common/observer_listener.dart index 5e9dfb7..f68fb4e 100644 --- a/lib/src/common/observer_listener.dart +++ b/lib/src/common/observer_listener.dart @@ -6,12 +6,12 @@ import 'dart:collection'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/src/common/models/observe_model.dart'; import 'package:scrollview_observer/src/common/observer_typedef.dart'; -class ObserverListenerEntry +base class ObserverListenerEntry extends LinkedListEntry> { ObserverListenerEntry({ required this.context, diff --git a/lib/src/common/observer_notification_result.dart b/lib/src/common/observer_notification_result.dart index 9a5a48b..5fc3dbb 100644 --- a/lib/src/common/observer_notification_result.dart +++ b/lib/src/common/observer_notification_result.dart @@ -4,13 +4,15 @@ * @Date: 2023-08-12 20:09:46 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/src/common/models/observe_model.dart'; import 'package:scrollview_observer/src/common/models/observer_handle_contexts_result_model.dart'; import 'package:scrollview_observer/src/common/typedefs.dart'; -class CommonOnceObserveNotificationResult> { +class CommonOnceObserveNotificationResult< + M extends ObserveModel, + R extends ObserverHandleContextsResultModel +> { bool get isSuccess => ObserverWidgetObserveResultType.success == type; /// Observation result type. diff --git a/lib/src/common/observer_typedef.dart b/lib/src/common/observer_typedef.dart index f5d962a..1438093 100644 --- a/lib/src/common/observer_typedef.dart +++ b/lib/src/common/observer_typedef.dart @@ -4,7 +4,7 @@ * @Date: 2022-12-04 15:57:38 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/src/common/models/observe_model.dart'; import 'package:scrollview_observer/src/common/models/observe_scroll_to_index_result_model.dart'; import 'package:scrollview_observer/src/sliver/models/sliver_viewport_observe_model.dart'; @@ -12,14 +12,13 @@ import 'package:scrollview_observer/src/sliver/models/sliver_viewport_observe_mo /// Called when the ObserverController prepare to scroll to index with /// [ObservePrepareScrollToIndexModel]. typedef ObserverOnPrepareScrollToIndex = Future Function( - ObservePrepareScrollToIndexModel); + ObservePrepareScrollToIndexModel, +); /// The callback type of getting observed result for first sliver. /// /// Corresponds to onObserve. -typedef OnObserveCallback = void Function( - M result, -); +typedef OnObserveCallback = void Function(M result); /// The callback type of getting observed result map. /// @@ -36,17 +35,10 @@ typedef OnObserveViewportCallback = void Function( ); /// Define type that auto trigger observe. -enum ObserverAutoTriggerObserveType { - scrollStart, - scrollUpdate, - scrollEnd, -} +enum ObserverAutoTriggerObserveType { scrollStart, scrollUpdate, scrollEnd } /// Define type that trigger [onObserve] callback. -enum ObserverTriggerOnObserveType { - directly, - displayingItemsChange, -} +enum ObserverTriggerOnObserveType { directly, displayingItemsChange } /// Define type of the observed render sliver. enum ObserverRenderSliverType { diff --git a/lib/src/common/observer_widget.dart b/lib/src/common/observer_widget.dart index dd35c28..3e93442 100644 --- a/lib/src/common/observer_widget.dart +++ b/lib/src/common/observer_widget.dart @@ -8,7 +8,7 @@ import 'dart:async'; import 'dart:collection'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/src/common/models/observer_handle_contexts_result_model.dart'; @@ -23,8 +23,12 @@ import 'package:scrollview_observer/src/utils/src/log.dart'; import 'models/observe_model.dart'; -class ObserverWidget extends StatefulWidget { +class ObserverWidget< + C extends ObserverController, + M extends ObserveModel, + N extends ScrollViewOnceObserveNotification +> + extends StatefulWidget { /// The subtree below this widget. final Widget child; @@ -97,7 +101,7 @@ class ObserverWidget 0 && toNextOverPercent <= 1), - super(key: key); + }) : assert(toNextOverPercent > 0 && toNextOverPercent <= 1); @override State createState() => @@ -137,19 +140,17 @@ class ObserverWidget? maybeOf< - C extends ObserverController, - M extends ObserveModel, - N extends ScrollViewOnceObserveNotification, - T extends ObserverWidget>( - BuildContext context, { - String? tag, - }) { - BuildContext? _ctx; + C extends ObserverController, + M extends ObserveModel, + N extends ScrollViewOnceObserveNotification, + T extends ObserverWidget + >(BuildContext context, {String? tag}) { + BuildContext? ctx; if (tag != null) { final tagManager = ObserverWidgetTagManager.maybeOf(context); - _ctx = tagManager?.context(tag); + ctx = tagManager?.context(tag); } - return (_ctx ?? context) + return (ctx ?? context) .dependOnInheritedWidgetOfExactType>() ?.observerWidgetState; } @@ -171,17 +172,12 @@ class ObserverWidget of< - C extends ObserverController, - M extends ObserveModel, - N extends ScrollViewOnceObserveNotification, - T extends ObserverWidget>( - BuildContext context, { - String? tag, - }) { - final observerState = maybeOf( - context, - tag: tag, - ); + C extends ObserverController, + M extends ObserveModel, + N extends ScrollViewOnceObserveNotification, + T extends ObserverWidget + >(BuildContext context, {String? tag}) { + final observerState = maybeOf(context, tag: tag); assert(() { if (observerState == null) { throw FlutterError( @@ -202,10 +198,12 @@ class ObserverWidget> extends State { + C extends ObserverController, + M extends ObserveModel, + N extends ScrollViewOnceObserveNotification, + T extends ObserverWidget +> + extends State { /// Target sliver [BuildContext] List targetSliverContexts = []; @@ -218,7 +216,7 @@ class ObserverWidgetState< [ ObserverAutoTriggerObserveType.scrollStart, ObserverAutoTriggerObserveType.scrollUpdate, - ObserverAutoTriggerObserveType.scrollEnd + ObserverAutoTriggerObserveType.scrollEnd, ]; /// Mapping [ObserverAutoTriggerObserveType] to [ScrollNotification]. @@ -301,9 +299,9 @@ class ObserverWidgetState< // Placed at the deepest level for convenient subsequent operations using // its context. Widget resultWidget = ObserverWidgetScope( - child: widget.child, observerWidgetState: this, onCreateElement: _handleScopeContext, + child: widget.child, ); resultWidget = NotificationListener( onNotification: (notification) { @@ -331,8 +329,9 @@ class ObserverWidgetState< // If the notification.runtimeType is not in the list of // innerAutoTriggerObserveScrollNotifications that can trigger // observation, the notification will be ignored. - if (innerAutoTriggerObserveScrollNotifications - .contains(notification.runtimeType)) { + if (innerAutoTriggerObserveScrollNotifications.contains( + notification.runtimeType, + )) { final isIgnoreInnerCanHandleObserve = ScrollUpdateNotification != notification.runtimeType; WidgetsBinding.instance.endOfFrame.then((_) { @@ -370,9 +369,7 @@ class ObserverWidgetState< // When nesting multiple ObserverWidgets, ensure that only one // ObserverWidgetTagManager is at the top. if (ObserverWidgetTagManager.maybeOf(context) == null) { - resultWidget = ObserverWidgetTagManager( - child: resultWidget, - ); + resultWidget = ObserverWidgetTagManager(child: resultWidget); } return resultWidget; } @@ -406,11 +403,11 @@ class ObserverWidgetState< if (sliverListContexts != null) { ctxs = sliverListContexts(); } else { - List _ctxs = []; + List ctxs0 = []; void visitor(Element element) { if (isTargetSliverContextType(element.renderObject)) { /// Find the target sliver context - _ctxs.add(element); + ctxs0.add(element); return; } element.visitChildren(visitor); @@ -426,7 +423,7 @@ class ObserverWidgetState< ); } - ctxs = _ctxs; + ctxs = ctxs0; } } return ctxs; @@ -560,7 +557,7 @@ class ObserverWidgetState< final tag = widget.tag ?? ''; if (tag.isEmpty) return; await WidgetsBinding.instance.endOfFrame; - assert(ctx.mounted); + if (!ctx.mounted) return; final tagManager = ObserverWidgetTagManager.maybeOf(ctx); tagManager?.set(tag, ctx); } @@ -574,13 +571,12 @@ class ObserverWidgetState< // ObserverWidgetTagManager. await (innerCheckTagChangeEndOfFrame ?? WidgetsBinding.instance.endOfFrame); if (!mounted) return; - final _scopeContext = scopeContext; - if (_scopeContext == null) return; - assert(_scopeContext.mounted); - final tagManager = ObserverWidgetTagManager.maybeOf(_scopeContext); + final scopeContext = this.scopeContext; + if (scopeContext == null || !scopeContext.mounted) return; + final tagManager = ObserverWidgetTagManager.maybeOf(scopeContext); tagManager?.remove(oldTag); if (tag.isNotEmpty) { - tagManager?.set(tag, _scopeContext); + tagManager?.set(tag, scopeContext); } } @@ -596,11 +592,13 @@ class ObserverWidgetState< onObserve != null || onObserveAll != null, 'At least one callback must be provided.', ); - innerListeners?.add(ObserverListenerEntry( - context: context, - onObserve: onObserve, - onObserveAll: onObserveAll, - )); + innerListeners?.add( + ObserverListenerEntry( + context: context, + onObserve: onObserve, + onObserveAll: onObserveAll, + ), + ); } /// Remove the specified [OnObserveCallback] and [OnObserveAllCallback]. @@ -614,9 +612,9 @@ class ObserverWidgetState< onObserve != null || onObserveAll != null, 'At least one callback must be provided.', ); - final _listeners = innerListeners; - if (_listeners == null) return; - for (final ObserverListenerEntry entry in _listeners) { + final listeners = innerListeners; + if (listeners == null) return; + for (final ObserverListenerEntry entry in listeners) { if (entry.context == context && entry.onObserve == onObserve && entry.onObserveAll == onObserveAll) { @@ -626,15 +624,13 @@ class ObserverWidgetState< } } - void _notifyListeners( - Map changeResultMap, - ) { + void _notifyListeners(Map changeResultMap) { if (changeResultMap.isEmpty) return; - final _listeners = innerListeners; - if (_listeners == null || _listeners.isEmpty) return; + final listeners = innerListeners; + if (listeners == null || listeners.isEmpty) return; final List> localListeners = - List>.of(_listeners); + List>.of(listeners); for (final ObserverListenerEntry entry in localListeners) { try { if (entry.list != null) { @@ -643,30 +639,33 @@ class ObserverWidgetState< if (entry.onObserve != null) { // If sliverContext is not specified, the first one in // targetSliverContexts is taken. - BuildContext? _sliverContext = entry.context; - if (_sliverContext == null && targetSliverContexts.isNotEmpty) { - _sliverContext = targetSliverContexts.first; + BuildContext? sliverContext = entry.context; + if (sliverContext == null && targetSliverContexts.isNotEmpty) { + sliverContext = targetSliverContexts.first; } - final result = changeResultMap[_sliverContext]; + final result = changeResultMap[sliverContext]; if (result == null) continue; entry.onObserve?.call(result); } } } catch (exception, stack) { - FlutterError.reportError(FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'scrollview_observer', - context: - ErrorDescription('while dispatching result for $runtimeType'), - informationCollector: () => [ - DiagnosticsProperty( - 'The $runtimeType sending result was', - this, - style: DiagnosticsTreeStyle.errorProperty, + FlutterError.reportError( + FlutterErrorDetails( + exception: exception, + stack: stack, + library: 'scrollview_observer', + context: ErrorDescription( + 'while dispatching result for $runtimeType', ), - ], - )); + informationCollector: () => [ + DiagnosticsProperty( + 'The $runtimeType sending result was', + this, + style: DiagnosticsTreeStyle.errorProperty, + ), + ], + ), + ); } } } diff --git a/lib/src/common/observer_widget_scope.dart b/lib/src/common/observer_widget_scope.dart index d66fbc5..20789d7 100644 --- a/lib/src/common/observer_widget_scope.dart +++ b/lib/src/common/observer_widget_scope.dart @@ -4,23 +4,25 @@ * @Date: 2024-10-19 11:49:39 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer/src/common/observer_controller.dart'; import 'package:scrollview_observer/src/common/observer_widget.dart'; class ObserverWidgetScope< - C extends ObserverController, - M extends ObserveModel, - N extends ScrollViewOnceObserveNotification, - T extends ObserverWidget> extends InheritedWidget { + C extends ObserverController, + M extends ObserveModel, + N extends ScrollViewOnceObserveNotification, + T extends ObserverWidget +> + extends InheritedWidget { const ObserverWidgetScope({ - Key? key, - required Widget child, + super.key, + required super.child, required this.observerWidgetState, required this.onCreateElement, - }) : super(key: key, child: child); + }); /// The [ObserverWidgetState] instance. final ObserverWidgetState observerWidgetState; diff --git a/lib/src/common/observer_widget_tag_manager.dart b/lib/src/common/observer_widget_tag_manager.dart index 54bb3cc..5f006fd 100644 --- a/lib/src/common/observer_widget_tag_manager.dart +++ b/lib/src/common/observer_widget_tag_manager.dart @@ -4,15 +4,12 @@ * @Date: 2024-11-03 14:40:40 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; class ObserverWidgetTagManager extends InheritedWidget { final Map _tagMap = {}; - ObserverWidgetTagManager({ - Key? key, - required Widget child, - }) : super(key: key, child: child); + ObserverWidgetTagManager({super.key, required super.child}); /// Getting the [ObserverWidgetTagManager] instance. /// @@ -23,10 +20,7 @@ class ObserverWidgetTagManager extends InheritedWidget { } /// Setting the tag and context. - void set( - String tag, - BuildContext context, - ) { + void set(String tag, BuildContext context) { _tagMap[tag] = context; } @@ -36,9 +30,7 @@ class ObserverWidgetTagManager extends InheritedWidget { } /// Getting the context by tag. - BuildContext? context( - String tag, - ) { + BuildContext? context(String tag) { return _tagMap[tag]; } diff --git a/lib/src/common/typedefs.dart b/lib/src/common/typedefs.dart index 2cfca93..a373f73 100644 --- a/lib/src/common/typedefs.dart +++ b/lib/src/common/typedefs.dart @@ -18,10 +18,8 @@ T? ambiguate(T? value) => value; /// /// The [targetOffset] property is the offset of the planned locate. typedef ObserverLocateIndexOffsetCallback = double Function( - double targetOffset); + double targetOffset, +); /// Observation result types in ObserverWidget. -enum ObserverWidgetObserveResultType { - success, - interrupted, -} +enum ObserverWidgetObserveResultType { success, interrupted } diff --git a/lib/src/gridview/grid_observer_controller.dart b/lib/src/gridview/grid_observer_controller.dart index 97c9233..6f59345 100644 --- a/lib/src/gridview/grid_observer_controller.dart +++ b/lib/src/gridview/grid_observer_controller.dart @@ -3,7 +3,7 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-07-20 00:32:40 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer/src/common/models/observer_handle_contexts_result_model.dart'; @@ -15,12 +15,11 @@ class GridObserverController extends ObserverController ObserverControllerForInfo, ObserverControllerForScroll, ObserverControllerForNotification< - GridViewObserveModel, - ObserverHandleContextsResultModel, - GridViewOnceObserveNotificationResult> { - GridObserverController({ - ScrollController? controller, - }) : super(controller: controller); + GridViewObserveModel, + ObserverHandleContextsResultModel, + GridViewOnceObserveNotificationResult + > { + GridObserverController({super.controller}); /// Dispatch a [GridViewOnceObserveNotification] Future dispatchOnceObserve({ @@ -72,10 +71,10 @@ class GridObserverController extends ObserverController /// Create a observation notification result. @override GridViewOnceObserveNotificationResult - innerCreateOnceObserveNotificationResult({ + innerCreateOnceObserveNotificationResult({ required ObserverWidgetObserveResultType resultType, required ObserverHandleContextsResultModel? - resultModel, + resultModel, }) { return GridViewOnceObserveNotificationResult( type: resultType, diff --git a/lib/src/gridview/grid_observer_notification_result.dart b/lib/src/gridview/grid_observer_notification_result.dart index 960befd..7607ebd 100644 --- a/lib/src/gridview/grid_observer_notification_result.dart +++ b/lib/src/gridview/grid_observer_notification_result.dart @@ -6,19 +6,20 @@ import 'package:scrollview_observer/src/common/models/observer_handle_contexts_result_model.dart'; import 'package:scrollview_observer/src/common/observer_notification_result.dart'; -import 'package:scrollview_observer/src/common/typedefs.dart'; import 'package:scrollview_observer/src/gridview/models/gridview_observe_model.dart'; class GridViewOnceObserveNotificationResult - extends CommonOnceObserveNotificationResult> { + extends + CommonOnceObserveNotificationResult< + GridViewObserveModel, + ObserverHandleContextsResultModel + > { GridViewOnceObserveNotificationResult({ - required ObserverWidgetObserveResultType type, + required super.type, required ObserverHandleContextsResultModel - observeResult, + observeResult, }) : super( - type: type, - observeResult: observeResult.changeResultModel, - observeAllResult: observeResult.changeResultMap, - ); + observeResult: observeResult.changeResultModel, + observeAllResult: observeResult.changeResultMap, + ); } diff --git a/lib/src/gridview/grid_observer_view.dart b/lib/src/gridview/grid_observer_view.dart index 25adcca..643a80f 100644 --- a/lib/src/gridview/grid_observer_view.dart +++ b/lib/src/gridview/grid_observer_view.dart @@ -3,99 +3,82 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/src/common/observer_widget.dart'; -import 'package:scrollview_observer/src/common/observer_typedef.dart'; import 'package:scrollview_observer/src/notification.dart'; import 'package:scrollview_observer/src/observer_core.dart'; import 'grid_observer_controller.dart'; import 'models/gridview_observe_model.dart'; -class GridViewObserver extends ObserverWidget { +class GridViewObserver + extends + ObserverWidget< + GridObserverController, + GridViewObserveModel, + GridViewOnceObserveNotification + > { /// The callback of getting all sliverGrid's buildContext. final List Function()? sliverGridContexts; final GridObserverController? controller; const GridViewObserver({ - Key? key, - required Widget child, - String? tag, + super.key, + required super.child, + super.tag, this.sliverGridContexts, this.controller, - OnObserveAllCallback? onObserveAll, - OnObserveCallback? onObserve, - double leadingOffset = 0, - double Function()? dynamicLeadingOffset, - double toNextOverPercent = 1, - ScrollNotificationPredicate? scrollNotificationPredicate, - List? autoTriggerObserveTypes, - ObserverTriggerOnObserveType triggerOnObserveType = - ObserverTriggerOnObserveType.displayingItemsChange, - GridViewObserveModel? Function(BuildContext context)? customHandleObserve, - bool Function(RenderObject?)? customTargetRenderSliverType, - bool cancelOnceObserveNotificationBubbling = true, - }) : super( - key: key, - child: child, - tag: tag, - sliverContexts: sliverGridContexts, - sliverController: controller, - onObserveAll: onObserveAll, - onObserve: onObserve, - leadingOffset: leadingOffset, - dynamicLeadingOffset: dynamicLeadingOffset, - toNextOverPercent: toNextOverPercent, - scrollNotificationPredicate: scrollNotificationPredicate, - autoTriggerObserveTypes: autoTriggerObserveTypes, - triggerOnObserveType: triggerOnObserveType, - customHandleObserve: customHandleObserve, - customTargetRenderSliverType: customTargetRenderSliverType, - cancelOnceObserveNotificationBubbling: - cancelOnceObserveNotificationBubbling, - ); + super.onObserveAll, + super.onObserve, + super.leadingOffset, + super.dynamicLeadingOffset, + super.toNextOverPercent, + super.scrollNotificationPredicate, + super.autoTriggerObserveTypes, + super.triggerOnObserveType, + super.customHandleObserve, + super.customTargetRenderSliverType, + super.cancelOnceObserveNotificationBubbling, + }) : super(sliverContexts: sliverGridContexts, sliverController: controller); @override State createState() => GridViewObserverState(); - static GridViewObserverState? maybeOf( - BuildContext context, { - String? tag, - }) { - final _state = ObserverWidget.maybeOf< - GridObserverController, - GridViewObserveModel, - GridViewOnceObserveNotification, - GridViewObserver>( - context, - tag: tag, - ); - if (_state is! GridViewObserverState) return null; - return _state; + static GridViewObserverState? maybeOf(BuildContext context, {String? tag}) { + final state = + ObserverWidget.maybeOf< + GridObserverController, + GridViewObserveModel, + GridViewOnceObserveNotification, + GridViewObserver + >(context, tag: tag); + if (state is! GridViewObserverState) return null; + return state; } - static GridViewObserverState of( - BuildContext context, { - String? tag, - }) { - final _state = ObserverWidget.of< - GridObserverController, - GridViewObserveModel, - GridViewOnceObserveNotification, - GridViewObserver>( - context, - tag: tag, - ); - return _state as GridViewObserverState; + static GridViewObserverState of(BuildContext context, {String? tag}) { + final state = + ObserverWidget.of< + GridObserverController, + GridViewObserveModel, + GridViewOnceObserveNotification, + GridViewObserver + >(context, tag: tag); + return state as GridViewObserverState; } } -class GridViewObserverState extends ObserverWidgetState { +class GridViewObserverState + extends + ObserverWidgetState< + GridObserverController, + GridViewObserveModel, + GridViewOnceObserveNotification, + GridViewObserver + > { @override GridViewObserveModel? handleObserve(BuildContext ctx) { if (widget.customHandleObserve != null) { diff --git a/lib/src/gridview/models/gridview_observe_displaying_child_model.dart b/lib/src/gridview/models/gridview_observe_displaying_child_model.dart index c486858..12729a7 100644 --- a/lib/src/gridview/models/gridview_observe_displaying_child_model.dart +++ b/lib/src/gridview/models/gridview_observe_displaying_child_model.dart @@ -11,15 +11,10 @@ class GridViewObserveDisplayingChildModel extends ObserveDisplayingChildModel with ObserveDisplayingChildModelMixin { GridViewObserveDisplayingChildModel({ required this.sliverGrid, - required RenderViewportBase viewport, - required int index, - required RenderBox renderObject, - }) : super( - sliver: sliverGrid, - viewport: viewport, - index: index, - renderObject: renderObject, - ); + required super.viewport, + required super.index, + required super.renderObject, + }) : super(sliver: sliverGrid); /// The target sliverGrid RenderSliverMultiBoxAdaptor sliverGrid; diff --git a/lib/src/gridview/models/gridview_observe_model.dart b/lib/src/gridview/models/gridview_observe_model.dart index 3711992..04f2c4e 100644 --- a/lib/src/gridview/models/gridview_observe_model.dart +++ b/lib/src/gridview/models/gridview_observe_model.dart @@ -11,18 +11,16 @@ import 'package:scrollview_observer/src/gridview/models/gridview_observe_display class GridViewObserveModel extends ObserveModel { GridViewObserveModel({ required this.sliverGrid, - required RenderViewportBase viewport, + required super.viewport, required this.firstGroupChildList, required this.displayingChildModelList, required this.displayingChildModelMap, - required bool visible, + required super.visible, }) : super( - visible: visible, - sliver: sliverGrid, - viewport: viewport, - innerDisplayingChildModelList: displayingChildModelList, - innerDisplayingChildModelMap: displayingChildModelMap, - ); + sliver: sliverGrid, + innerDisplayingChildModelList: displayingChildModelList, + innerDisplayingChildModelMap: displayingChildModelMap, + ); /// The target sliverGrid. RenderSliverMultiBoxAdaptor sliverGrid; @@ -42,7 +40,9 @@ class GridViewObserveModel extends ObserveModel { if (other is GridViewObserveModel) { return listEquals(firstGroupChildList, other.firstGroupChildList) && listEquals( - displayingChildModelList, other.displayingChildModelList) && + displayingChildModelList, + other.displayingChildModelList, + ) && mapEquals(displayingChildModelMap, other.displayingChildModelMap); } else { return false; diff --git a/lib/src/listview/list_observer_controller.dart b/lib/src/listview/list_observer_controller.dart index e236674..d732614 100644 --- a/lib/src/listview/list_observer_controller.dart +++ b/lib/src/listview/list_observer_controller.dart @@ -3,7 +3,7 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-07-20 00:32:40 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer/src/common/models/observer_handle_contexts_result_model.dart'; @@ -15,12 +15,11 @@ class ListObserverController extends ObserverController ObserverControllerForInfo, ObserverControllerForScroll, ObserverControllerForNotification< - ListViewObserveModel, - ObserverHandleContextsResultModel, - ListViewOnceObserveNotificationResult> { - ListObserverController({ - ScrollController? controller, - }) : super(controller: controller); + ListViewObserveModel, + ObserverHandleContextsResultModel, + ListViewOnceObserveNotificationResult + > { + ListObserverController({super.controller}); /// Dispatch a [ListViewOnceObserveNotification] Future dispatchOnceObserve({ @@ -72,10 +71,10 @@ class ListObserverController extends ObserverController /// Create a observation notification result. @override ListViewOnceObserveNotificationResult - innerCreateOnceObserveNotificationResult({ + innerCreateOnceObserveNotificationResult({ required ObserverWidgetObserveResultType resultType, required ObserverHandleContextsResultModel? - resultModel, + resultModel, }) { return ListViewOnceObserveNotificationResult( type: resultType, diff --git a/lib/src/listview/list_observer_notification_result.dart b/lib/src/listview/list_observer_notification_result.dart index a969e6c..64268f9 100644 --- a/lib/src/listview/list_observer_notification_result.dart +++ b/lib/src/listview/list_observer_notification_result.dart @@ -6,19 +6,20 @@ import 'package:scrollview_observer/src/common/models/observer_handle_contexts_result_model.dart'; import 'package:scrollview_observer/src/common/observer_notification_result.dart'; -import 'package:scrollview_observer/src/common/typedefs.dart'; import 'package:scrollview_observer/src/listview/models/listview_observe_model.dart'; class ListViewOnceObserveNotificationResult - extends CommonOnceObserveNotificationResult> { + extends + CommonOnceObserveNotificationResult< + ListViewObserveModel, + ObserverHandleContextsResultModel + > { ListViewOnceObserveNotificationResult({ - required ObserverWidgetObserveResultType type, + required super.type, required ObserverHandleContextsResultModel - observeResult, + observeResult, }) : super( - type: type, - observeResult: observeResult.changeResultModel, - observeAllResult: observeResult.changeResultMap, - ); + observeResult: observeResult.changeResultModel, + observeAllResult: observeResult.changeResultMap, + ); } diff --git a/lib/src/listview/list_observer_view.dart b/lib/src/listview/list_observer_view.dart index 06473fc..691f7a4 100644 --- a/lib/src/listview/list_observer_view.dart +++ b/lib/src/listview/list_observer_view.dart @@ -3,10 +3,9 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; -import 'package:scrollview_observer/src/common/observer_typedef.dart'; import 'package:scrollview_observer/src/common/observer_widget.dart'; import 'package:scrollview_observer/src/listview/models/listview_observe_model.dart'; import 'package:scrollview_observer/src/notification.dart'; @@ -14,50 +13,36 @@ import 'package:scrollview_observer/src/observer_core.dart'; import 'list_observer_controller.dart'; -class ListViewObserver extends ObserverWidget { +class ListViewObserver + extends + ObserverWidget< + ListObserverController, + ListViewObserveModel, + ListViewOnceObserveNotification + > { /// The callback of getting all sliverList's buildContext. final List Function()? sliverListContexts; final ListObserverController? controller; const ListViewObserver({ - Key? key, - required Widget child, - String? tag, + super.key, + required super.child, + super.tag, this.controller, this.sliverListContexts, - OnObserveAllCallback? onObserveAll, - OnObserveCallback? onObserve, - double leadingOffset = 0, - double Function()? dynamicLeadingOffset, - double toNextOverPercent = 1, - ScrollNotificationPredicate? scrollNotificationPredicate, - List? autoTriggerObserveTypes, - ObserverTriggerOnObserveType triggerOnObserveType = - ObserverTriggerOnObserveType.displayingItemsChange, - ListViewObserveModel? Function(BuildContext context)? customHandleObserve, - bool Function(RenderObject?)? customTargetRenderSliverType, - bool cancelOnceObserveNotificationBubbling = true, - }) : super( - key: key, - child: child, - tag: tag, - sliverController: controller, - sliverContexts: sliverListContexts, - onObserveAll: onObserveAll, - onObserve: onObserve, - leadingOffset: leadingOffset, - dynamicLeadingOffset: dynamicLeadingOffset, - toNextOverPercent: toNextOverPercent, - scrollNotificationPredicate: scrollNotificationPredicate, - autoTriggerObserveTypes: autoTriggerObserveTypes, - triggerOnObserveType: triggerOnObserveType, - customHandleObserve: customHandleObserve, - customTargetRenderSliverType: customTargetRenderSliverType, - cancelOnceObserveNotificationBubbling: - cancelOnceObserveNotificationBubbling, - ); + super.onObserveAll, + super.onObserve, + super.leadingOffset, + super.dynamicLeadingOffset, + super.toNextOverPercent, + super.scrollNotificationPredicate, + super.autoTriggerObserveTypes, + super.triggerOnObserveType, + super.customHandleObserve, + super.customTargetRenderSliverType, + super.cancelOnceObserveNotificationBubbling, + }) : super(sliverController: controller, sliverContexts: sliverListContexts); @override State createState() => ListViewObserverState(); @@ -77,20 +62,16 @@ class ListViewObserver extends ObserverWidget( - context, - tag: tag, - ); - if (_state is! ListViewObserverState) return null; - return _state; + static ListViewObserverState? maybeOf(BuildContext context, {String? tag}) { + final state = + ObserverWidget.maybeOf< + ListObserverController, + ListViewObserveModel, + ListViewOnceObserveNotification, + ListViewObserver + >(context, tag: tag); + if (state is! ListViewObserverState) return null; + return state; } /// Returning the closest instance of this class that encloses the given @@ -109,19 +90,15 @@ class ListViewObserver extends ObserverWidget( - context, - tag: tag, - ); - return _state as ListViewObserverState; + static ListViewObserverState of(BuildContext context, {String? tag}) { + final state = + ObserverWidget.of< + ListObserverController, + ListViewObserveModel, + ListViewOnceObserveNotification, + ListViewObserver + >(context, tag: tag); + return state as ListViewObserverState; } /// Determine whether the [obj] is a supported RenderSliver type. @@ -140,8 +117,14 @@ class ListViewObserver extends ObserverWidget { +class ListViewObserverState + extends + ObserverWidgetState< + ListObserverController, + ListViewObserveModel, + ListViewOnceObserveNotification, + ListViewObserver + > { @override ListViewObserveModel? handleObserve(BuildContext ctx) { if (widget.customHandleObserve != null) { diff --git a/lib/src/listview/models/listview_observe_displaying_child_model.dart b/lib/src/listview/models/listview_observe_displaying_child_model.dart index 04ea056..1b5ff27 100644 --- a/lib/src/listview/models/listview_observe_displaying_child_model.dart +++ b/lib/src/listview/models/listview_observe_displaying_child_model.dart @@ -11,15 +11,10 @@ class ListViewObserveDisplayingChildModel extends ObserveDisplayingChildModel with ObserveDisplayingChildModelMixin { ListViewObserveDisplayingChildModel({ required this.sliverList, - required RenderViewportBase viewport, - required int index, - required RenderBox renderObject, - }) : super( - sliver: sliverList, - viewport: viewport, - index: index, - renderObject: renderObject, - ); + required super.viewport, + required super.index, + required super.renderObject, + }) : super(sliver: sliverList); /// The target sliverList. /// It would be [RenderSliverList] or [RenderSliverFixedExtentList]. diff --git a/lib/src/listview/models/listview_observe_model.dart b/lib/src/listview/models/listview_observe_model.dart index cf15c79..4fa6144 100644 --- a/lib/src/listview/models/listview_observe_model.dart +++ b/lib/src/listview/models/listview_observe_model.dart @@ -12,18 +12,16 @@ import 'listview_observe_displaying_child_model.dart'; class ListViewObserveModel extends ObserveModel { ListViewObserveModel({ required this.sliverList, - required RenderViewportBase viewport, + required super.viewport, required this.firstChild, required this.displayingChildModelList, required this.displayingChildModelMap, - required bool visible, + required super.visible, }) : super( - visible: visible, - sliver: sliverList, - viewport: viewport, - innerDisplayingChildModelList: displayingChildModelList, - innerDisplayingChildModelMap: displayingChildModelMap, - ); + sliver: sliverList, + innerDisplayingChildModelList: displayingChildModelList, + innerDisplayingChildModelMap: displayingChildModelMap, + ); /// The target sliverList. /// It would be [RenderSliverList] or [RenderSliverFixedExtentList]. @@ -44,7 +42,9 @@ class ListViewObserveModel extends ObserveModel { if (other is ListViewObserveModel) { return firstChild == other.firstChild && listEquals( - displayingChildModelList, other.displayingChildModelList) && + displayingChildModelList, + other.displayingChildModelList, + ) && mapEquals(displayingChildModelMap, other.displayingChildModelMap); } else { return false; diff --git a/lib/src/notification.dart b/lib/src/notification.dart index cb6baec..2355b00 100644 --- a/lib/src/notification.dart +++ b/lib/src/notification.dart @@ -3,7 +3,7 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-05-28 12:37:41 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/src/common/observer_controller.dart'; class ScrollViewOnceObserveNotification extends Notification { @@ -25,24 +25,18 @@ class ScrollViewOnceObserveNotification extends Notification { class ListViewOnceObserveNotification extends ScrollViewOnceObserveNotification { ListViewOnceObserveNotification({ - bool isForce = false, - bool isDependObserveCallback = true, - }) : super( - isForce: isForce, - isDependObserveCallback: isDependObserveCallback, - ); + super.isForce, + super.isDependObserveCallback, + }); } /// The Notification for Triggering an GridView observation class GridViewOnceObserveNotification extends ScrollViewOnceObserveNotification { GridViewOnceObserveNotification({ - bool isForce = false, - bool isDependObserveCallback = true, - }) : super( - isForce: isForce, - isDependObserveCallback: isDependObserveCallback, - ); + super.isForce, + super.isDependObserveCallback, + }); } /// A notification of scrolling task. diff --git a/lib/src/observer_core.dart b/lib/src/observer_core.dart index d289e60..7e1d0bf 100644 --- a/lib/src/observer_core.dart +++ b/lib/src/observer_core.dart @@ -6,7 +6,7 @@ import 'dart:math'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'gridview/models/gridview_observe_displaying_child_model.dart'; @@ -23,18 +23,18 @@ class ObserverCore { double? Function(BuildContext)? customOverlap, double toNextOverPercent = 1, }) { - var _obj = ObserverUtils.findRenderObject(context); - if (_obj is! RenderSliverMultiBoxAdaptor) return null; - final viewport = ObserverUtils.findViewport(_obj); + var obj = ObserverUtils.findRenderObject(context); + if (obj is! RenderSliverMultiBoxAdaptor) return null; + final viewport = ObserverUtils.findViewport(obj); if (viewport == null) return null; if (kDebugMode) { if (viewport.debugNeedsPaint) return null; } // The geometry.visible is not absolutely reliable. - if (!(_obj.geometry?.visible ?? false) || - _obj.constraints.remainingPaintExtent < 1e-10) { + if (!(obj.geometry?.visible ?? false) || + obj.constraints.remainingPaintExtent < 1e-10) { return ListViewObserveModel( - sliverList: _obj, + sliverList: obj, viewport: viewport, visible: false, firstChild: null, @@ -42,13 +42,13 @@ class ObserverCore { displayingChildModelMap: {}, ); } - final scrollDirection = _obj.constraints.axis; - var firstChild = _obj.firstChild; + final scrollDirection = obj.constraints.axis; + var firstChild = obj.firstChild; if (firstChild == null) return null; final offset = fetchLeadingOffset?.call() ?? 0; - final overlap = customOverlap?.call(context) ?? _obj.constraints.overlap; - final rawScrollViewOffset = _obj.constraints.scrollOffset + overlap; + final overlap = customOverlap?.call(context) ?? obj.constraints.overlap; + final rawScrollViewOffset = obj.constraints.scrollOffset + overlap; var scrollViewOffset = rawScrollViewOffset + offset; var parentData = firstChild.parentData as SliverMultiBoxAdaptorParentData; var index = parentData.index ?? 0; @@ -65,7 +65,7 @@ class ObserverCore { toNextOverPercent: toNextOverPercent, )) { index = index + 1; - var nextChild = _obj.childAfter(targetFirstChild); + var nextChild = obj.childAfter(targetFirstChild); if (nextChild == null) { isNotFound = true; break; @@ -73,7 +73,7 @@ class ObserverCore { if (nextChild is! RenderIndexedSemantics) { // It is separator - nextChild = _obj.childAfter(nextChild); + nextChild = obj.childAfter(nextChild); } if (nextChild == null) { isNotFound = true; @@ -86,7 +86,7 @@ class ObserverCore { // ScrollView is not visible. if (isNotFound) { return ListViewObserveModel( - sliverList: _obj, + sliverList: obj, viewport: viewport, visible: false, firstChild: null, @@ -99,7 +99,7 @@ class ObserverCore { final firstDisplayingChildIndex = targetFirstChild.index; final firstDisplayingChildModel = ListViewObserveDisplayingChildModel( - sliverList: _obj, + sliverList: obj, viewport: viewport, index: firstDisplayingChildIndex, renderObject: targetFirstChild, @@ -113,8 +113,8 @@ class ObserverCore { // Find the remaining children that are being displayed final showingChildrenMaxOffset = - rawScrollViewOffset + _obj.constraints.remainingPaintExtent - overlap; - var displayingChild = _obj.childAfter(targetFirstChild); + rawScrollViewOffset + obj.constraints.remainingPaintExtent - overlap; + var displayingChild = obj.childAfter(targetFirstChild); while (ObserverUtils.isDisplayingChildInSliver( targetChild: displayingChild, showingChildrenMaxOffset: showingChildrenMaxOffset, @@ -127,24 +127,24 @@ class ObserverCore { } if (displayingChild is! RenderIndexedSemantics) { // It is separator - displayingChild = _obj.childAfter(displayingChild); + displayingChild = obj.childAfter(displayingChild); continue; } final displayingChildIndex = displayingChild.index; final displayingChildModel = ListViewObserveDisplayingChildModel( - sliverList: _obj, + sliverList: obj, viewport: viewport, index: displayingChildIndex, renderObject: displayingChild, ); displayingChildModelList.add(displayingChildModel); displayingChildModelMap[displayingChildIndex] = displayingChildModel; - displayingChild = _obj.childAfter(displayingChild); + displayingChild = obj.childAfter(displayingChild); } return ListViewObserveModel( - sliverList: _obj, + sliverList: obj, viewport: viewport, visible: true, firstChild: firstDisplayingChildModel, @@ -160,18 +160,18 @@ class ObserverCore { double? Function(BuildContext)? customOverlap, double toNextOverPercent = 1, }) { - final _obj = ObserverUtils.findRenderObject(context); - if (_obj is! RenderSliverMultiBoxAdaptor) return null; - final viewport = ObserverUtils.findViewport(_obj); + final obj = ObserverUtils.findRenderObject(context); + if (obj is! RenderSliverMultiBoxAdaptor) return null; + final viewport = ObserverUtils.findViewport(obj); if (viewport == null) return null; if (kDebugMode) { if (viewport.debugNeedsPaint) return null; } // The geometry.visible is not absolutely reliable. - if (!(_obj.geometry?.visible ?? false) || - _obj.constraints.remainingPaintExtent < 1e-10) { + if (!(obj.geometry?.visible ?? false) || + obj.constraints.remainingPaintExtent < 1e-10) { return GridViewObserveModel( - sliverGrid: _obj, + sliverGrid: obj, viewport: viewport, visible: false, firstGroupChildList: [], @@ -179,13 +179,13 @@ class ObserverCore { displayingChildModelMap: {}, ); } - final scrollDirection = _obj.constraints.axis; - var firstChild = _obj.firstChild; + final scrollDirection = obj.constraints.axis; + var firstChild = obj.firstChild; if (firstChild == null) return null; final offset = fetchLeadingOffset?.call() ?? 0; - final overlap = customOverlap?.call(context) ?? _obj.constraints.overlap; - final rawScrollViewOffset = _obj.constraints.scrollOffset + overlap; + final overlap = customOverlap?.call(context) ?? obj.constraints.overlap; + final rawScrollViewOffset = obj.constraints.scrollOffset + overlap; var scrollViewOffset = rawScrollViewOffset + offset; // Whether the first child being displayed is not found. @@ -201,7 +201,7 @@ class ObserverCore { toNextOverPercent: toNextOverPercent, )) { /// Entering here means it is not the target object - RenderBox? nextChild = _obj.childAfter(targetFirstChild); + RenderBox? nextChild = obj.childAfter(targetFirstChild); if (nextChild == null) { isNotFound = true; break; @@ -213,7 +213,7 @@ class ObserverCore { // ScrollView is not visible. if (isNotFound) { return GridViewObserveModel( - sliverGrid: _obj, + sliverGrid: obj, viewport: viewport, visible: false, firstGroupChildList: [], @@ -227,7 +227,7 @@ class ObserverCore { final firstDisplayingChildIndex = targetFirstChild.index; final firstModel = GridViewObserveDisplayingChildModel( - sliverGrid: _obj, + sliverGrid: obj, viewport: viewport, index: firstDisplayingChildIndex, renderObject: targetFirstChild, @@ -240,10 +240,10 @@ class ObserverCore { ]; final showingChildrenMaxOffset = - rawScrollViewOffset + _obj.constraints.remainingPaintExtent - overlap; + rawScrollViewOffset + obj.constraints.remainingPaintExtent - overlap; // Find out other child those have reached the specified offset. - RenderBox? targetChild = _obj.childAfter(targetFirstChild); + RenderBox? targetChild = obj.childAfter(targetFirstChild); while (targetChild != null) { if (ObserverUtils.isReachOffsetWidgetInSliver( scrollViewOffset: max(scrollViewOffset, firstModel.layoutOffset), @@ -254,7 +254,7 @@ class ObserverCore { if (targetChild is! RenderIndexedSemantics) break; final targetChildIndex = targetChild.index; final displayingChildModel = GridViewObserveDisplayingChildModel( - sliverGrid: _obj, + sliverGrid: obj, viewport: viewport, index: targetChildIndex, renderObject: targetChild, @@ -264,16 +264,17 @@ class ObserverCore { lastFirstGroupChildWidget = targetChild; } - RenderBox? nextChild = _obj.childAfter(targetChild); + RenderBox? nextChild = obj.childAfter(targetChild); if (nextChild == null) break; targetChild = nextChild; } - List showingChildModelList = - List.from(firstGroupChildModelList); + List showingChildModelList = List.from( + firstGroupChildModelList, + ); // Find the remaining children that are being displayed - var displayingChild = _obj.childAfter(lastFirstGroupChildWidget); + var displayingChild = obj.childAfter(lastFirstGroupChildWidget); while (displayingChild != null) { if (ObserverUtils.isDisplayingChildInSliver( targetChild: displayingChild, @@ -287,7 +288,7 @@ class ObserverCore { } final displayingChildIndex = displayingChild.index; final displayingChildModel = GridViewObserveDisplayingChildModel( - sliverGrid: _obj, + sliverGrid: obj, viewport: viewport, index: displayingChildIndex, renderObject: displayingChild, @@ -295,11 +296,11 @@ class ObserverCore { showingChildModelList.add(displayingChildModel); displayingChildModelMap[displayingChildIndex] = displayingChildModel; } - displayingChild = _obj.childAfter(displayingChild); + displayingChild = obj.childAfter(displayingChild); } return GridViewObserveModel( - sliverGrid: _obj, + sliverGrid: obj, viewport: viewport, visible: true, firstGroupChildList: firstGroupChildModelList, diff --git a/lib/src/sliver/models/sliver_observer_observe_result_model.dart b/lib/src/sliver/models/sliver_observer_observe_result_model.dart index 617082a..90dcfa0 100644 --- a/lib/src/sliver/models/sliver_observer_observe_result_model.dart +++ b/lib/src/sliver/models/sliver_observer_observe_result_model.dart @@ -4,7 +4,7 @@ * @Date: 2023-08-12 16:18:26 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/src/common/models/observe_model.dart'; import 'package:scrollview_observer/src/common/models/observer_handle_contexts_result_model.dart'; import 'package:scrollview_observer/src/sliver/models/sliver_viewport_observe_model.dart'; @@ -21,7 +21,7 @@ class SliverObserverHandleContextsResultModel Map changeResultMap = const {}, this.observeViewportResultModel, }) : super( - changeResultModel: changeResultModel, - changeResultMap: changeResultMap, - ); + changeResultModel: changeResultModel, + changeResultMap: changeResultMap, + ); } diff --git a/lib/src/sliver/models/sliver_viewport_observe_displaying_child_model.dart b/lib/src/sliver/models/sliver_viewport_observe_displaying_child_model.dart index af72828..22b8ad4 100644 --- a/lib/src/sliver/models/sliver_viewport_observe_displaying_child_model.dart +++ b/lib/src/sliver/models/sliver_viewport_observe_displaying_child_model.dart @@ -3,7 +3,7 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2023-05-14 10:51:42 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; class SliverViewportObserveDisplayingChildModel { diff --git a/lib/src/sliver/models/sliver_viewport_observe_model.dart b/lib/src/sliver/models/sliver_viewport_observe_model.dart index 55094e6..659830f 100644 --- a/lib/src/sliver/models/sliver_viewport_observe_model.dart +++ b/lib/src/sliver/models/sliver_viewport_observe_model.dart @@ -17,7 +17,7 @@ class SliverViewportObserveModel { /// Stores observing model list of displaying children widgets. final List - displayingChildModelList; + displayingChildModelList; SliverViewportObserveModel({ required this.viewport, diff --git a/lib/src/sliver/sliver_observer_controller.dart b/lib/src/sliver/sliver_observer_controller.dart index 500366c..bddaa92 100644 --- a/lib/src/sliver/sliver_observer_controller.dart +++ b/lib/src/sliver/sliver_observer_controller.dart @@ -3,7 +3,7 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-08-08 00:20:03 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer/src/common/observer_controller.dart'; import 'package:scrollview_observer/src/common/typedefs.dart'; @@ -14,15 +14,14 @@ class SliverObserverController extends ObserverController ObserverControllerForInfo, ObserverControllerForScroll, ObserverControllerForNotification< - ObserveModel, - SliverObserverHandleContextsResultModel, - ScrollViewOnceObserveNotificationResult> { + ObserveModel, + SliverObserverHandleContextsResultModel, + ScrollViewOnceObserveNotificationResult + > { /// Whether to forbid the onObserveViewport callback. bool isForbidObserveViewportCallback = false; - SliverObserverController({ - ScrollController? controller, - }) : super(controller: controller); + SliverObserverController({super.controller}); /// Dispatch a [ScrollViewOnceObserveNotification] Future dispatchOnceObserve({ @@ -42,7 +41,7 @@ class SliverObserverController extends ObserverController /// Create a observation notification result. @override ScrollViewOnceObserveNotificationResult - innerCreateOnceObserveNotificationResult({ + innerCreateOnceObserveNotificationResult({ required ObserverWidgetObserveResultType resultType, required SliverObserverHandleContextsResultModel? resultModel, }) { diff --git a/lib/src/sliver/sliver_observer_listener.dart b/lib/src/sliver/sliver_observer_listener.dart index 338d9be..79af9fa 100644 --- a/lib/src/sliver/sliver_observer_listener.dart +++ b/lib/src/sliver/sliver_observer_listener.dart @@ -6,11 +6,11 @@ import 'dart:collection'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/src/common/observer_typedef.dart'; -class SliverObserverListenerEntry +base class SliverObserverListenerEntry extends LinkedListEntry { SliverObserverListenerEntry({ required this.context, diff --git a/lib/src/sliver/sliver_observer_notification_result.dart b/lib/src/sliver/sliver_observer_notification_result.dart index 8b54e4f..f36066f 100644 --- a/lib/src/sliver/sliver_observer_notification_result.dart +++ b/lib/src/sliver/sliver_observer_notification_result.dart @@ -6,22 +6,23 @@ import 'package:scrollview_observer/src/common/models/observe_model.dart'; import 'package:scrollview_observer/src/common/observer_notification_result.dart'; -import 'package:scrollview_observer/src/common/typedefs.dart'; import 'package:scrollview_observer/src/sliver/models/sliver_observer_observe_result_model.dart'; import 'package:scrollview_observer/src/sliver/models/sliver_viewport_observe_model.dart'; class ScrollViewOnceObserveNotificationResult - extends CommonOnceObserveNotificationResult> { + extends + CommonOnceObserveNotificationResult< + ObserveModel, + SliverObserverHandleContextsResultModel + > { ScrollViewOnceObserveNotificationResult({ - required ObserverWidgetObserveResultType type, + required super.type, required SliverObserverHandleContextsResultModel - observeResult, + observeResult, }) : super( - type: type, - observeResult: observeResult.changeResultModel, - observeAllResult: observeResult.changeResultMap, - ) { + observeResult: observeResult.changeResultModel, + observeAllResult: observeResult.changeResultMap, + ) { observeViewportResultModel = observeResult.observeViewportResultModel; } diff --git a/lib/src/sliver/sliver_observer_view.dart b/lib/src/sliver/sliver_observer_view.dart index 02e1c0b..2445619 100644 --- a/lib/src/sliver/sliver_observer_view.dart +++ b/lib/src/sliver/sliver_observer_view.dart @@ -6,7 +6,7 @@ import 'dart:collection'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/src/common/models/observe_model.dart'; @@ -23,8 +23,13 @@ import 'package:scrollview_observer/src/utils/observer_utils.dart'; import 'models/sliver_viewport_observe_model.dart'; import 'sliver_observer_controller.dart'; -class SliverViewObserver extends ObserverWidget { +class SliverViewObserver + extends + ObserverWidget< + SliverObserverController, + ObserveModel, + ScrollViewOnceObserveNotification + > { /// The callback of getting all slivers those are displayed in viewport. final OnObserveViewportCallback? onObserveViewport; @@ -41,46 +46,32 @@ class SliverViewObserver extends ObserverWidget Function()? sliverListContexts, List Function()? sliverContexts, - OnObserveAllCallback? onObserveAll, - OnObserveCallback? onObserve, + super.onObserveAll, + super.onObserve, this.onObserveViewport, - double leadingOffset = 0, - double Function()? dynamicLeadingOffset, + super.leadingOffset, + super.dynamicLeadingOffset, this.customOverlap, - double toNextOverPercent = 1, - ScrollNotificationPredicate? scrollNotificationPredicate, - List? autoTriggerObserveTypes, - ObserverTriggerOnObserveType triggerOnObserveType = - ObserverTriggerOnObserveType.displayingItemsChange, - ObserveModel? Function(BuildContext context)? customHandleObserve, + super.toNextOverPercent, + super.scrollNotificationPredicate, + super.autoTriggerObserveTypes, + super.triggerOnObserveType, + super.customHandleObserve, this.extendedHandleObserve, - bool cancelOnceObserveNotificationBubbling = true, + super.cancelOnceObserveNotificationBubbling, }) : super( - key: key, - child: child, - tag: tag, - sliverController: controller, - sliverContexts: sliverContexts ?? sliverListContexts, - onObserveAll: onObserveAll, - onObserve: onObserve, - leadingOffset: leadingOffset, - dynamicLeadingOffset: dynamicLeadingOffset, - toNextOverPercent: toNextOverPercent, - scrollNotificationPredicate: scrollNotificationPredicate, - autoTriggerObserveTypes: autoTriggerObserveTypes, - triggerOnObserveType: triggerOnObserveType, - customHandleObserve: customHandleObserve, - cancelOnceObserveNotificationBubbling: - cancelOnceObserveNotificationBubbling, - ); + sliverController: controller, + sliverContexts: sliverContexts ?? sliverListContexts, + ); @override State createState() => MixViewObserverState(); @@ -100,17 +91,16 @@ class SliverViewObserver extends ObserverWidget( - context, - tag: tag, - ); - if (_state is! MixViewObserverState) return null; - return _state; + static MixViewObserverState? maybeOf(BuildContext context, {String? tag}) { + final state = + ObserverWidget.maybeOf< + SliverObserverController, + ObserveModel, + ScrollViewOnceObserveNotification, + SliverViewObserver + >(context, tag: tag); + if (state is! MixViewObserverState) return null; + return state; } /// Returning the closest instance of this class that encloses the given @@ -129,21 +119,26 @@ class SliverViewObserver extends ObserverWidget( - context, - tag: tag, - ); - return _state as MixViewObserverState; + static MixViewObserverState of(BuildContext context, {String? tag}) { + final state = + ObserverWidget.of< + SliverObserverController, + ObserveModel, + ScrollViewOnceObserveNotification, + SliverViewObserver + >(context, tag: tag); + return state as MixViewObserverState; } } -class MixViewObserverState extends ObserverWidgetState { +class MixViewObserverState + extends + ObserverWidgetState< + SliverObserverController, + ObserveModel, + ScrollViewOnceObserveNotification, + SliverViewObserver + > { /// The last viewport observation result. SliverViewportObserveModel? lastViewportObserveResultModel; @@ -221,15 +216,15 @@ class MixViewObserverState extends ObserverWidgetState displayingChildModelList = [ - firstChild + firstChild, ]; // Find the remaining children that are being displayed. @@ -328,10 +324,12 @@ class MixViewObserverState extends ObserverWidgetState [ - DiagnosticsProperty( - 'The $runtimeType sending result was', - this, - style: DiagnosticsTreeStyle.errorProperty, + FlutterError.reportError( + FlutterErrorDetails( + exception: exception, + stack: stack, + library: 'scrollview_observer', + context: ErrorDescription( + 'while dispatching result for $runtimeType', ), - ], - )); + informationCollector: () => [ + DiagnosticsProperty( + 'The $runtimeType sending result was', + this, + style: DiagnosticsTreeStyle.errorProperty, + ), + ], + ), + ); } } } diff --git a/lib/src/utils/src/chat/chat_observer_scroll_physics.dart b/lib/src/utils/src/chat/chat_observer_scroll_physics.dart index b76ad28..bba38b1 100644 --- a/lib/src/utils/src/chat/chat_observer_scroll_physics.dart +++ b/lib/src/utils/src/chat/chat_observer_scroll_physics.dart @@ -4,28 +4,28 @@ * @Date: 2022-09-27 23:12:45 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; + import 'chat_observer_scroll_physics_mixin.dart'; import 'chat_scroll_observer.dart'; /// Deprecated scroll physics for chat observer clamping scroll physics. /// Use [ChatObserverClampingScrollPhysics] instead. @Deprecated( - 'It will be removed in version 2, please use [ChatObserverClampingScrollPhysics] instead') + 'It will be removed in version 2, please use [ChatObserverClampingScrollPhysics] instead', +) class ChatObserverClampinScrollPhysics extends ChatObserverClampingScrollPhysics { /// Creates a [ChatObserverClampinScrollPhysics]. - ChatObserverClampinScrollPhysics({ - required ChatScrollObserver observer, - }) : super(observer: observer); + ChatObserverClampinScrollPhysics({required super.observer}); } class ChatObserverClampingScrollPhysics extends ClampingScrollPhysics with ChatObserverScrollPhysicsMixin { ChatObserverClampingScrollPhysics({ - ScrollPhysics? parent, + super.parent, required ChatScrollObserver observer, - }) : super(parent: parent) { + }) { this.observer = observer; } @@ -43,9 +43,9 @@ class ChatObserverBouncingScrollPhysics extends BouncingScrollPhysics with ChatObserverScrollPhysicsMixin { /// Creates a [ChatObserverBouncingScrollPhysics]. ChatObserverBouncingScrollPhysics({ - ScrollPhysics? parent, + super.parent, required ChatScrollObserver observer, - }) : super(parent: parent) { + }) { this.observer = observer; } diff --git a/lib/src/utils/src/chat/chat_observer_scroll_physics_mixin.dart b/lib/src/utils/src/chat/chat_observer_scroll_physics_mixin.dart index 08885b4..249bcda 100644 --- a/lib/src/utils/src/chat/chat_observer_scroll_physics_mixin.dart +++ b/lib/src/utils/src/chat/chat_observer_scroll_physics_mixin.dart @@ -3,7 +3,7 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2022-09-29 22:47:10 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer/src/common/typedefs.dart'; @@ -30,11 +30,13 @@ mixin ChatObserverScrollPhysicsMixin on ScrollPhysics { if (newPosition.extentBefore <= observer.fixedPositionOffset || !isNeedFixedPosition || observer.isRemove) { - _handlePositionCallback(ChatScrollObserverHandlePositionResultModel( - type: ChatScrollObserverHandlePositionType.none, - mode: observer.innerMode, - changeCount: observer.changeCount, - )); + _handlePositionCallback( + ChatScrollObserverHandlePositionResultModel( + type: ChatScrollObserverHandlePositionType.none, + mode: observer.innerMode, + changeCount: observer.changeCount, + ), + ); return adjustPosition; } @@ -50,29 +52,35 @@ mixin ChatObserverScrollPhysicsMixin on ScrollPhysics { ), ); if (customAdjustPosition != null) { - _handlePositionCallback(ChatScrollObserverHandlePositionResultModel( - type: ChatScrollObserverHandlePositionType.keepPosition, - mode: observer.innerMode, - changeCount: observer.changeCount, - )); + _handlePositionCallback( + ChatScrollObserverHandlePositionResultModel( + type: ChatScrollObserverHandlePositionType.keepPosition, + mode: observer.innerMode, + changeCount: observer.changeCount, + ), + ); return customAdjustPosition; } final model = observer.observeRefItem(); if (model == null) { - _handlePositionCallback(ChatScrollObserverHandlePositionResultModel( - type: ChatScrollObserverHandlePositionType.none, - mode: observer.innerMode, - changeCount: observer.changeCount, - )); + _handlePositionCallback( + ChatScrollObserverHandlePositionResultModel( + type: ChatScrollObserverHandlePositionType.none, + mode: observer.innerMode, + changeCount: observer.changeCount, + ), + ); return adjustPosition; } - _handlePositionCallback(ChatScrollObserverHandlePositionResultModel( - type: ChatScrollObserverHandlePositionType.keepPosition, - mode: observer.innerMode, - changeCount: observer.changeCount, - )); + _handlePositionCallback( + ChatScrollObserverHandlePositionResultModel( + type: ChatScrollObserverHandlePositionType.keepPosition, + mode: observer.innerMode, + changeCount: observer.changeCount, + ), + ); // Customize the delta of the adjustPosition. double? customDelta = observer.customAdjustPositionDelta?.call( diff --git a/lib/src/utils/src/chat/chat_scroll_observer.dart b/lib/src/utils/src/chat/chat_scroll_observer.dart index fdb2cf7..f3f5f35 100644 --- a/lib/src/utils/src/chat/chat_scroll_observer.dart +++ b/lib/src/utils/src/chat/chat_scroll_observer.dart @@ -4,7 +4,7 @@ * @Date: 2022-09-27 23:01:58 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer/src/common/typedefs.dart'; @@ -60,7 +60,8 @@ class ChatScrollObserver { /// This callback will be called when handling in [ClampingScrollPhysics]'s /// [adjustPositionForNewDimensions]. @Deprecated( - 'It will be removed in version 2, please use [onHandlePositionResultCallback] instead') + 'It will be removed in version 2, please use [onHandlePositionResultCallback] instead', + ) void Function(ChatScrollObserverHandlePositionType)? onHandlePositionCallback; /// The result callback for processing chat location. @@ -68,7 +69,7 @@ class ChatScrollObserver { /// This callback will be called when handling in [ClampingScrollPhysics]'s /// [adjustPositionForNewDimensions]. void Function(ChatScrollObserverHandlePositionResultModel)? - onHandlePositionResultCallback; + onHandlePositionResultCallback; /// The mode of processing. ChatScrollObserverHandleMode innerMode = ChatScrollObserverHandleMode.normal; @@ -116,10 +117,12 @@ class ChatScrollObserver { ChatScrollObserverRefIndexType refIndexType = ChatScrollObserverRefIndexType.relativeIndexStartFromCacheExtent, @Deprecated( - 'It will be removed in version 2, please use [refItemIndex] instead') + 'It will be removed in version 2, please use [refItemIndex] instead', + ) int refItemRelativeIndex = 0, @Deprecated( - 'It will be removed in version 2, please use [refItemIndexAfterUpdate] instead') + 'It will be removed in version 2, please use [refItemIndexAfterUpdate] instead', + ) int refItemRelativeIndexAfterUpdate = 0, int refItemIndex = 0, int refItemIndexAfterUpdate = 0, @@ -135,18 +138,18 @@ class ChatScrollObserver { observeSwitchShrinkWrap(); } - int _innerRefItemIndex; - int _innerRefItemIndexAfterUpdate; - double _innerRefItemLayoutOffset; + int innerRefItemIndex; + int innerRefItemIndexAfterUpdate; + double innerRefItemLayoutOffset; switch (mode) { case ChatScrollObserverHandleMode.normal: final firstItemModel = observerController.observeFirstItem( sliverContext: sliverContext, ); if (firstItemModel == null) return; - _innerRefItemIndex = firstItemModel.index; - _innerRefItemIndexAfterUpdate = _innerRefItemIndex + changeCount; - _innerRefItemLayoutOffset = firstItemModel.layoutOffset; + innerRefItemIndex = firstItemModel.index; + innerRefItemIndexAfterUpdate = innerRefItemIndex + changeCount; + innerRefItemLayoutOffset = firstItemModel.layoutOffset; break; case ChatScrollObserverHandleMode.generative: final firstItemModel = observerController.observeFirstItem( @@ -159,15 +162,16 @@ class ChatScrollObserver { index: index, ); if (model == null) return; - _innerRefItemIndex = index; - _innerRefItemIndexAfterUpdate = index; - _innerRefItemLayoutOffset = model.layoutOffset; + innerRefItemIndex = index; + innerRefItemIndexAfterUpdate = index; + innerRefItemLayoutOffset = model.layoutOffset; break; case ChatScrollObserverHandleMode.specified: // Prioritize the values ​​of [refItemIndex] and [refItemIndexAfterUpdate] - int _refItemIndex = - refItemIndex != 0 ? refItemIndex : refItemRelativeIndex; - int _refItemIndexAfterUpdate = refItemIndexAfterUpdate != 0 + int refItemIndex0 = refItemIndex != 0 + ? refItemIndex + : refItemRelativeIndex; + int refItemIndexAfterUpdate0 = refItemIndexAfterUpdate != 0 ? refItemIndexAfterUpdate : refItemRelativeIndexAfterUpdate; @@ -177,16 +181,16 @@ class ChatScrollObserver { sliverContext: sliverContext, ); if (firstItemModel == null) return; - int index = firstItemModel.index + _refItemIndex; + int index = firstItemModel.index + refItemIndex0; final model = observerController.observeItem( sliverContext: sliverContext, index: index, ); if (model == null) return; - _innerRefItemIndex = index; - _innerRefItemIndexAfterUpdate = - firstItemModel.index + _refItemIndexAfterUpdate; - _innerRefItemLayoutOffset = model.layoutOffset; + innerRefItemIndex = index; + innerRefItemIndexAfterUpdate = + firstItemModel.index + refItemIndexAfterUpdate0; + innerRefItemLayoutOffset = model.layoutOffset; break; case ChatScrollObserverRefIndexType.relativeIndexStartFromDisplaying: final observeResult = await observerController.dispatchOnceObserve( @@ -194,36 +198,37 @@ class ChatScrollObserver { isDependObserveCallback: false, ); if (!observeResult.isSuccess) return; + if (sliverContext != null && !sliverContext.mounted) return; final currentFirstDisplayingChildIndex = observeResult.observeResult?.firstChild?.index ?? 0; - int index = currentFirstDisplayingChildIndex + _refItemIndex; + int index = currentFirstDisplayingChildIndex + refItemIndex0; final model = observerController.observeItem( sliverContext: sliverContext, index: index, ); if (model == null) return; - _innerRefItemIndex = index; - _innerRefItemIndexAfterUpdate = - currentFirstDisplayingChildIndex + _refItemIndexAfterUpdate; - _innerRefItemLayoutOffset = model.layoutOffset; + innerRefItemIndex = index; + innerRefItemIndexAfterUpdate = + currentFirstDisplayingChildIndex + refItemIndexAfterUpdate0; + innerRefItemLayoutOffset = model.layoutOffset; break; case ChatScrollObserverRefIndexType.itemIndex: final model = observerController.observeItem( sliverContext: sliverContext, - index: _refItemIndex, + index: refItemIndex0, ); if (model == null) return; - _innerRefItemIndex = _refItemIndex; - _innerRefItemIndexAfterUpdate = _refItemIndexAfterUpdate; - _innerRefItemLayoutOffset = model.layoutOffset; + innerRefItemIndex = refItemIndex0; + innerRefItemIndexAfterUpdate = refItemIndexAfterUpdate0; + innerRefItemLayoutOffset = model.layoutOffset; break; } } // Record value. innerIsNeedFixedPosition = true; - innerRefItemIndex = _innerRefItemIndex; - innerRefItemIndexAfterUpdate = _innerRefItemIndexAfterUpdate; - innerRefItemLayoutOffset = _innerRefItemLayoutOffset; + this.innerRefItemIndex = innerRefItemIndex; + this.innerRefItemIndexAfterUpdate = innerRefItemIndexAfterUpdate; + this.innerRefItemLayoutOffset = innerRefItemLayoutOffset; this.customAdjustPosition = customAdjustPosition; this.customAdjustPositionDelta = customAdjustPositionDelta; @@ -239,7 +244,7 @@ class ChatScrollObserver { // Related issue // https://github.com/fluttercandies/flutter_scrollview_observer/issues/64 final ctx = observerController.fetchSliverContext(); - if (ctx == null) return; + if (ctx == null || !ctx.mounted) return; final obj = ObserverUtils.findRenderObject(ctx); if (obj == null) return; final viewport = ObserverUtils.findViewport(obj); diff --git a/lib/src/utils/src/chat/chat_scroll_observer_model.dart b/lib/src/utils/src/chat/chat_scroll_observer_model.dart index 264489e..f42d028 100644 --- a/lib/src/utils/src/chat/chat_scroll_observer_model.dart +++ b/lib/src/utils/src/chat/chat_scroll_observer_model.dart @@ -3,7 +3,7 @@ * @Repo: https://github.com/LinXunFeng/flutter_scrollview_observer * @Date: 2023-05-13 10:33:00 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:scrollview_observer/src/common/models/observe_displaying_child_model_mixin.dart'; import 'package:scrollview_observer/src/utils/src/chat/chat_scroll_observer.dart'; diff --git a/lib/src/utils/src/extends.dart b/lib/src/utils/src/extends.dart index 112496e..8f75862 100644 --- a/lib/src/utils/src/extends.dart +++ b/lib/src/utils/src/extends.dart @@ -11,9 +11,7 @@ extension ObserverDouble on double { /// /// If the growthDirection is [GrowthDirection.forward], the value is /// returned directly, otherwise the opposite value is returned. - double rectify( - RenderSliver obj, - ) { + double rectify(RenderSliver obj) { return obj.isForwardGrowthDirection ? this : -this; } } diff --git a/lib/src/utils/src/nested_scroll_util.dart b/lib/src/utils/src/nested_scroll_util.dart index 5b29dd9..f391ce5 100644 --- a/lib/src/utils/src/nested_scroll_util.dart +++ b/lib/src/utils/src/nested_scroll_util.dart @@ -4,7 +4,7 @@ * @Date: 2023-12-04 20:15:33 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer/src/common/typedefs.dart'; @@ -69,12 +69,12 @@ class NestedScrollUtil { final remainingContextOverlap = remainingSliverRenderObj!.constraints.overlap; - final sliverContextExtraOverlap = - (remainingContextOverlap - offset.dy).clamp(0, double.infinity); + final sliverContextExtraOverlap = (remainingContextOverlap - offset.dy) + .clamp(0, double.infinity); - var _obj = ObserverUtils.findRenderObject(sliverContext); - if (_obj is! RenderSliverMultiBoxAdaptor) return null; - return sliverContextExtraOverlap + _obj.constraints.overlap; + var obj = ObserverUtils.findRenderObject(sliverContext); + if (obj is! RenderSliverMultiBoxAdaptor) return null; + return sliverContextExtraOverlap + obj.constraints.overlap; } /// Calculate the [precedingScrollExtent] for [sliverContext]. @@ -83,9 +83,9 @@ class NestedScrollUtil { required BuildContext sliverContext, }) { double precedingScrollExtent = 0; - var _obj = ObserverUtils.findRenderObject(sliverContext); - if (_obj is! RenderSliverMultiBoxAdaptor) return null; - precedingScrollExtent = _obj.constraints.precedingScrollExtent; + var obj = ObserverUtils.findRenderObject(sliverContext); + if (obj is! RenderSliverMultiBoxAdaptor) return null; + precedingScrollExtent = obj.constraints.precedingScrollExtent; // Get SliverFillRemaining final remainingSliverContext = fetchRemainingSliverContext( diff --git a/lib/src/utils/src/observer_utils.dart b/lib/src/utils/src/observer_utils.dart index 033942f..a1e4009 100644 --- a/lib/src/utils/src/observer_utils.dart +++ b/lib/src/utils/src/observer_utils.dart @@ -4,13 +4,14 @@ * @Date: 2022-08-21 00:53:44 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:scrollview_observer/src/common/models/observe_model.dart'; import 'package:scrollview_observer/src/gridview/models/gridview_observe_displaying_child_model.dart'; import 'package:scrollview_observer/src/gridview/models/gridview_observe_model.dart'; import 'package:scrollview_observer/src/listview/models/listview_observe_model.dart'; import 'package:scrollview_observer/src/utils/src/log.dart'; + import 'dart:math' as math; class ObserverUtils { @@ -85,17 +86,19 @@ class ObserverUtils { return currentTabIndex; } var curIndex = indexes[currentTabIndex]; - final firstGroupIndexList = - firstGroupChildList.map((e) => e.index).toList(); + final firstGroupIndexList = firstGroupChildList + .map((e) => e.index) + .toList(); final minOffset = mainChildModel.layoutOffset; final maxOffset = mainChildModel.layoutOffset + mainChildModel.mainAxisSize; - final displayingChildModelList = - observeModel.displayingChildModelList.where((e) { - return !firstGroupIndexList.contains(e.index) && - e.layoutOffset >= minOffset && - e.layoutOffset <= maxOffset; - }).toList(); + final displayingChildModelList = observeModel.displayingChildModelList + .where((e) { + return !firstGroupIndexList.contains(e.index) && + e.layoutOffset >= minOffset && + e.layoutOffset <= maxOffset; + }) + .toList(); // If the indexes of all the children currently being displayed are // greater than curIndex, keep using currentTabIndex. // Otherwise, using targetTabIndex. @@ -316,9 +319,11 @@ class ObserverUtils { // It throws an exception when getting renderObject of inactive element. return context?.findRenderObject(); } catch (e) { - Log.warning('Cannot get renderObject of inactive element.\n' - 'Please call the reattach method of ObserverController to re-record ' - 'BuildContext.'); + Log.warning( + 'Cannot get renderObject of inactive element.\n' + 'Please call the reattach method of ObserverController to re-record ' + 'BuildContext.', + ); return null; } } @@ -374,17 +379,15 @@ class ObserverUtils { } /// Safely obtain [RenderSliver.constraints]. - static SliverConstraints? sliverConstraints( - RenderSliver sliver, - ) { - SliverConstraints? _constraints; + static SliverConstraints? sliverConstraints(RenderSliver sliver) { + SliverConstraints? constraints; try { - _constraints = sliver.constraints; + constraints = sliver.constraints; } catch (e) { Log.warning( 'A RenderObject does not have any constraints before it has been laid out.', ); } - return _constraints; + return constraints; } } diff --git a/lib/src/utils/src/slivers.dart b/lib/src/utils/src/slivers.dart index dc4c88a..62c1615 100644 --- a/lib/src/utils/src/slivers.dart +++ b/lib/src/utils/src/slivers.dart @@ -1,17 +1,13 @@ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; class SliverObserveContext extends SliverPadding { final void Function(BuildContext) onObserve; const SliverObserveContext({ - Key? key, + super.key, Widget? child, required this.onObserve, - }) : super( - key: key, - padding: EdgeInsets.zero, - sliver: child, - ); + }) : super(padding: EdgeInsets.zero, sliver: child); @override RenderSliverPadding createRenderObject(BuildContext context) { @@ -24,10 +20,10 @@ class SliverObserveContextToBoxAdapter extends SliverToBoxAdapter { final void Function(BuildContext) onObserve; const SliverObserveContextToBoxAdapter({ - Key? key, - required Widget? child, + super.key, + required super.child, required this.onObserve, - }) : super(key: key, child: child); + }); @override RenderSliverToBoxAdapter createRenderObject(BuildContext context) { diff --git a/pubspec.lock b/pubspec.lock index aa5d980..cf45ad4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + cupertino_ui: + dependency: transitive + description: + name: cupertino_ui + sha256: e9dfe7fac704028f8928cbe4028a0be5e8a709498e8daf8247de99e99a32aef3 + url: "https://pub.dev" + source: hosted + version: "1.0.2" fake_async: dependency: transitive description: @@ -58,15 +66,28 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493 + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "6.0.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + intl: + dependency: transitive + description: + name: intl + sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" + url: "https://pub.dev" + source: hosted + version: "0.20.3" leak_tracker: dependency: transitive description: @@ -95,18 +116,18 @@ packages: dependency: transitive description: name: lints - sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "6.1.0" matcher: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.20" material_color_utilities: dependency: transitive description: @@ -115,14 +136,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.13.0" + material_ui: + dependency: "direct main" + description: + name: material_ui + sha256: fbfb53cab6c4629438feeade5f3d305f72944bc9d9f25786b19106d32b80ec45 + url: "https://pub.dev" + source: hosted + version: "1.2.0" meta: dependency: transitive description: name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" path: dependency: transitive description: @@ -180,18 +209,18 @@ packages: dependency: transitive description: name: test_api - sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" + sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" url: "https://pub.dev" source: hosted - version: "0.7.11" + version: "0.7.12" vector_math: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.2" vm_service: dependency: transitive description: @@ -201,5 +230,5 @@ packages: source: hosted version: "15.0.0" sdks: - dart: ">=3.10.0-0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.13.0 <4.0.0" + flutter: ">=3.47.0" diff --git a/pubspec.yaml b/pubspec.yaml index 129e634..10b8759 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,17 +1,18 @@ name: scrollview_observer description: A widget for observing data related to the child widgets being displayed in a ScrollView. -version: 1.27.1 +version: 2.0.0 homepage: https://github.com/fluttercandies/flutter_scrollview_observer environment: - sdk: ">=2.19.0 <4.0.0" - flutter: '>=3.7.0' + sdk: ^3.13.0 + flutter: ">=3.47.0" dependencies: flutter: sdk: flutter + material_ui: ^1.2.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^6.0.0 diff --git a/test/chat_scroll_observer_test.dart b/test/chat_scroll_observer_test.dart index 1218eb4..6a36929 100644 --- a/test/chat_scroll_observer_test.dart +++ b/test/chat_scroll_observer_test.dart @@ -3,7 +3,7 @@ * @Repo: https://github.com/fluttercandies/flutter_scrollview_observer * @Date: 2023-11-25 19:04:30 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; @@ -11,8 +11,9 @@ void main() { // Regression test for https://github.com/fluttercandies/flutter_scrollview_observer/issues/64. testWidgets('Keeping position', (tester) async { final scrollController = ScrollController(); - final observerController = - ListObserverController(controller: scrollController); + final observerController = ListObserverController( + controller: scrollController, + ); final chatScrollObserver = ChatScrollObserver(observerController) ..fixedPositionOffset = -1; @@ -40,12 +41,14 @@ void main() { scrollController.dispose(); }); - testWidgets('Keeping position with ChatScrollObserverHandleMode.specified', - (tester) async { + testWidgets('Keeping position with ChatScrollObserverHandleMode.specified', ( + tester, + ) async { GlobalKey key = GlobalKey(); final scrollController = ScrollController(); - final observerController = - ListObserverController(controller: scrollController); + final observerController = ListObserverController( + controller: scrollController, + ); final chatScrollObserver = ChatScrollObserver(observerController) ..fixedPositionOffset = -1; const firstDisplayingChildIndex = 2; @@ -58,13 +61,8 @@ void main() { ); await tester.pumpWidget(widget); - updateData({ - int index = 0, - }) { - key.currentState?.updateData( - index: index, - needSetState: true, - ); + updateData({int index = 0}) { + key.currentState?.updateData(index: index, needSetState: true); } observerController.jumpTo(index: firstDisplayingChildIndex); @@ -90,10 +88,7 @@ void main() { refItemIndexAfterUpdate: 1, ); expect(chatScrollObserver.refItemIndex, firstItemIndex + 1); - expect( - chatScrollObserver.refItemIndexAfterUpdate, - firstItemIndex + 1, - ); + expect(chatScrollObserver.refItemIndexAfterUpdate, firstItemIndex + 1); updateData(); await tester.pumpAndSettle(); result = await observerController.dispatchOnceObserve( @@ -168,101 +163,89 @@ void main() { }); testWidgets( - 'Keeping position with ChatScrollObserverHandleMode.specified when item changes by itself', - (tester) async { - GlobalKey key = GlobalKey(); - final scrollController = ScrollController(); - final observerController = ListObserverController( - controller: scrollController, - ); - final chatScrollObserver = ChatScrollObserver(observerController) - ..fixedPositionOffset = -1; - const observeItemSelfIndex = 0; - - Widget widget = ChatListView( - key: key, - scrollController: scrollController, - observerController: observerController, - chatScrollObserver: chatScrollObserver, - itemBuilder: (context, index) { - final dataList = key.currentState?.dataList ?? []; - return Text( - dataList[index], - maxLines: 999, - ); - }, - dataList: ['initData' * 500], - ); - await tester.pumpWidget(widget); - - void updateData({ - int index = 0, - }) { - key.currentState?.updateData( - index: index, - needSetState: true, + 'Keeping position with ChatScrollObserverHandleMode.specified when item changes by itself', + (tester) async { + GlobalKey key = GlobalKey(); + final scrollController = ScrollController(); + final observerController = ListObserverController( + controller: scrollController, ); - } + final chatScrollObserver = ChatScrollObserver(observerController) + ..fixedPositionOffset = -1; + const observeItemSelfIndex = 0; + + Widget widget = ChatListView( + key: key, + scrollController: scrollController, + observerController: observerController, + chatScrollObserver: chatScrollObserver, + itemBuilder: (context, index) { + final dataList = key.currentState?.dataList ?? []; + return Text(dataList[index], maxLines: 999); + }, + dataList: ['initData' * 500], + ); + await tester.pumpWidget(widget); - await tester.pumpAndSettle(); - var result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - isForce: true, - ); - expectSync( - result.observeResult?.firstChild?.index ?? 0, - observeItemSelfIndex, - ); - expectSync( - result.observeResult?.firstChild?.mainAxisSize, - greaterThanOrEqualTo( - result.observeResult?.viewport.paintBounds.height ?? 0, - ), - ); - final previousTrailingMarginToViewport = - result.observeResult?.firstChild?.trailingMarginToViewport; + void updateData({int index = 0}) { + key.currentState?.updateData(index: index, needSetState: true); + } - // itemIndex - await chatScrollObserver.standby( - mode: ChatScrollObserverHandleMode.specified, - refIndexType: ChatScrollObserverRefIndexType.itemIndex, - refItemIndex: observeItemSelfIndex, - refItemIndexAfterUpdate: observeItemSelfIndex, - customAdjustPositionDelta: (model) { - return model.newPosition.extentAfter - model.oldPosition.extentAfter; - }, - ); + await tester.pumpAndSettle(); + var result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + isForce: true, + ); + expectSync( + result.observeResult?.firstChild?.index ?? 0, + observeItemSelfIndex, + ); + expectSync( + result.observeResult?.firstChild?.mainAxisSize, + greaterThanOrEqualTo( + result.observeResult?.viewport.paintBounds.height ?? 0, + ), + ); + final previousTrailingMarginToViewport = + result.observeResult?.firstChild?.trailingMarginToViewport; + + // itemIndex + await chatScrollObserver.standby( + mode: ChatScrollObserverHandleMode.specified, + refIndexType: ChatScrollObserverRefIndexType.itemIndex, + refItemIndex: observeItemSelfIndex, + refItemIndexAfterUpdate: observeItemSelfIndex, + customAdjustPositionDelta: (model) { + return model.newPosition.extentAfter - model.oldPosition.extentAfter; + }, + ); - updateData(); - await tester.pumpAndSettle(); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - isForce: true, - ); + updateData(); + await tester.pumpAndSettle(); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + isForce: true, + ); - expect(chatScrollObserver.refItemIndex, observeItemSelfIndex); - expect( - chatScrollObserver.refItemIndexAfterUpdate, - observeItemSelfIndex, - ); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - isForce: true, - ); - expectSync( - result.observeResult?.firstChild?.index, - observeItemSelfIndex, - ); - expectSync( - result.observeResult?.firstChild?.trailingMarginToViewport, - previousTrailingMarginToViewport, - ); + expect(chatScrollObserver.refItemIndex, observeItemSelfIndex); + expect(chatScrollObserver.refItemIndexAfterUpdate, observeItemSelfIndex); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + isForce: true, + ); + expectSync(result.observeResult?.firstChild?.index, observeItemSelfIndex); + expectSync( + result.observeResult?.firstChild?.trailingMarginToViewport, + previousTrailingMarginToViewport, + ); - scrollController.dispose(); - }); + scrollController.dispose(); + }, + ); - testWidgets('Keeping position with customAdjustPositionDelta', - (tester) async { + testWidgets('Keeping position with customAdjustPositionDelta', ( + tester, + ) async { GlobalKey key = GlobalKey(); final scrollController = ScrollController(); final observerController = ListObserverController( @@ -309,10 +292,7 @@ void main() { expect(observeResult?.firstChild?.leadingMarginToViewport, 0); // Check adjustPosition. - key.currentState?.updateData( - index: 1, - needSetState: true, - ); + key.currentState?.updateData(index: 1, needSetState: true); double? adjustPosition; await chatScrollObserver.standby( mode: ChatScrollObserverHandleMode.specified, @@ -447,10 +427,7 @@ void main() { expect(lastModel?.trailingMarginToViewport, 0); expect(onHandlePositionResultModel, isNull); - chatListViewState?.updateData( - index: lastIndex, - needSetState: true, - ); + chatListViewState?.updateData(index: lastIndex, needSetState: true); await chatScrollObserver.standby( customAdjustPosition: (model) { final delta = @@ -500,9 +477,7 @@ void main() { observeSwitchShrinkWrapCount = 0; // Test with isNeedObserveSwitchShrinkWrap = false - await chatScrollObserver.standby( - isNeedObserveSwitchShrinkWrap: false, - ); + await chatScrollObserver.standby(isNeedObserveSwitchShrinkWrap: false); expect(observeSwitchShrinkWrapCount, 0); // Test with isNeedObserveSwitchShrinkWrap = true (default) @@ -528,9 +503,7 @@ void main() { await tester.pumpAndSettle(); final testContext = tester.element(find.byType(ListView)); - await chatScrollObserver.standby( - sliverContext: testContext, - ); + await chatScrollObserver.standby(sliverContext: testContext); // Verify it is recorded expect(chatScrollObserver.innerSliverContext, testContext); @@ -560,9 +533,9 @@ class MockListObserverController extends ListObserverController { class TestChatScrollObserver extends ChatScrollObserver { TestChatScrollObserver( - ListObserverController observerController, { + super.observerController, { required this.onObserveSwitchShrinkWrap, - }) : super(observerController); + }); final VoidCallback onObserveSwitchShrinkWrap; @@ -575,14 +548,14 @@ class TestChatScrollObserver extends ChatScrollObserver { class ChatListView extends StatefulWidget { const ChatListView({ - Key? key, + super.key, required this.scrollController, required this.observerController, required this.chatScrollObserver, this.onReceiveScrollNotification, this.itemBuilder, this.dataList, - }) : super(key: key); + }); final ScrollController scrollController; final ListObserverController observerController; @@ -596,13 +569,11 @@ class ChatListView extends StatefulWidget { } class ChatListViewState extends State { - late List dataList = widget.dataList ?? + late List dataList = + widget.dataList ?? List.generate(100, (index) => index.toString()).toList(); - void initData({ - required List dataList, - bool needSetState = true, - }) { + void initData({required List dataList, bool needSetState = true}) { this.dataList = dataList; if (needSetState) { setState(() {}); @@ -662,7 +633,8 @@ class ChatListViewState extends State { observer: widget.chatScrollObserver, ), controller: widget.scrollController, - itemBuilder: widget.itemBuilder ?? + itemBuilder: + widget.itemBuilder ?? (context, index) { return SizedBox( height: 100, diff --git a/test/grid_observer_test.dart b/test/grid_observer_test.dart index ba5ff55..4133422 100644 --- a/test/grid_observer_test.dart +++ b/test/grid_observer_test.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer/src/common/observer_widget_tag_manager.dart'; @@ -27,9 +27,7 @@ void main() { } return Container( color: Colors.blue[100], - child: Center( - child: Text('index -- $index'), - ), + child: Center(child: Text('index -- $index')), ); }, itemCount: itemCount, @@ -42,12 +40,10 @@ void main() { final gridObserverController = GridObserverController( controller: scrollController, ); - Widget widget = getGridView( - scrollController: scrollController, - ); + Widget widget = getGridView(scrollController: scrollController); widget = GridViewObserver( - child: widget, controller: gridObserverController, + child: widget, ); await tester.pumpWidget(widget); expect(gridObserverController.sliverContexts.length, 1); @@ -79,12 +75,12 @@ void main() { }, ); widget = GridViewObserver( - child: widget, controller: observerController, scrollNotificationPredicate: defaultScrollNotificationPredicate, onObserve: (result) { isCalledOnObserve = true; }, + child: widget, ); await tester.pumpWidget(widget); @@ -117,16 +113,14 @@ void main() { controller: scrollController, ); - Widget widget = getGridView( - scrollController: scrollController, - ); + Widget widget = getGridView(scrollController: scrollController); GridViewObserveModel? observeResult; widget = GridViewObserver( - child: widget, controller: gridObserverController, onObserve: (result) { observeResult = result; }, + child: widget, ); await tester.pumpWidget(widget); @@ -134,8 +128,9 @@ void main() { gridObserverController.jumpTo(index: targetItemIndex); await tester.pumpAndSettle(); expect( - (observeResult?.firstGroupChildList.map((e) => e.index) ?? []) - .contains(targetItemIndex), + (observeResult?.firstGroupChildList.map((e) => e.index) ?? []).contains( + targetItemIndex, + ), true, ); @@ -147,8 +142,9 @@ void main() { ); await tester.pumpAndSettle(); expect( - (observeResult?.firstGroupChildList.map((e) => e.index) ?? []) - .contains(targetItemIndex), + (observeResult?.firstGroupChildList.map((e) => e.index) ?? []).contains( + targetItemIndex, + ), true, ); @@ -162,13 +158,8 @@ void main() { controller: scrollController, )..cacheJumpIndexOffset = false; - Widget widget = getGridView( - scrollController: scrollController, - ); - widget = GridViewObserver( - child: widget, - controller: observerController, - ); + Widget widget = getGridView(scrollController: scrollController); + widget = GridViewObserver(controller: observerController, child: widget); await tester.pumpWidget(widget); observerController.jumpTo(index: 30); @@ -185,13 +176,8 @@ void main() { controller: scrollController, ); - Widget widget = getGridView( - scrollController: scrollController, - ); - widget = GridViewObserver( - child: widget, - controller: observerController, - ); + Widget widget = getGridView(scrollController: scrollController); + widget = GridViewObserver(controller: observerController, child: widget); await tester.pumpWidget(widget); final ctx = observerController.fetchSliverContext(); @@ -259,20 +245,19 @@ void main() { var itemCount = 20; late StateSetter setStateFn; - Widget widget = StatefulBuilder(builder: (context, setState) { - setStateFn = setState; - return getGridViewWithItemCount( - scrollController: scrollController, - itemCount: itemCount, - itemExtent: itemExtent, - crossAxisCount: crossAxisCount, - spacing: spacing, - ); - }); - widget = GridViewObserver( - child: widget, - controller: observerController, + Widget widget = StatefulBuilder( + builder: (context, setState) { + setStateFn = setState; + return getGridViewWithItemCount( + scrollController: scrollController, + itemCount: itemCount, + itemExtent: itemExtent, + crossAxisCount: crossAxisCount, + spacing: spacing, + ); + }, ); + widget = GridViewObserver(controller: observerController, child: widget); await tester.pumpWidget(widget); await tester.pumpAndSettle(); @@ -305,43 +290,32 @@ void main() { controller: scrollController, ); - Widget widget = getGridView( - scrollController: scrollController, - ); + Widget widget = getGridView(scrollController: scrollController); GridViewObserveModel? observeResult; widget = GridViewObserver( - child: widget, controller: observerController, onObserve: (result) { observeResult = result; }, + child: widget, ); await tester.pumpWidget(widget); int targetItemIndex = 30; - observerController.jumpTo( - index: targetItemIndex, - alignment: 0, - ); + observerController.jumpTo(index: targetItemIndex, alignment: 0); await tester.pumpAndSettle(); var firstGroupChildList = observeResult?.firstGroupChildList ?? []; expect(firstGroupChildList, isNotEmpty); expect(firstGroupChildList.first.index, targetItemIndex); expect(firstGroupChildList.first.displayPercentage, 1); - observerController.jumpTo( - index: targetItemIndex, - alignment: 0.5, - ); + observerController.jumpTo(index: targetItemIndex, alignment: 0.5); await tester.pumpAndSettle(); firstGroupChildList = observeResult?.firstGroupChildList ?? []; expect(firstGroupChildList.first.index, targetItemIndex); expect(firstGroupChildList.first.displayPercentage, 0.5); - observerController.jumpTo( - index: targetItemIndex, - alignment: 1, - ); + observerController.jumpTo(index: targetItemIndex, alignment: 1); await tester.pumpAndSettle(); firstGroupChildList = observeResult?.firstGroupChildList ?? []; expect(firstGroupChildList.first.index, targetItemIndex + 2); @@ -356,11 +330,8 @@ void main() { )..observeIntervalForScrolling = const Duration(milliseconds: 500); int observeCount = 0; - Widget widget = getGridView( - scrollController: scrollController, - ); + Widget widget = getGridView(scrollController: scrollController); widget = GridViewObserver( - child: widget, controller: observerController, autoTriggerObserveTypes: const [ ObserverAutoTriggerObserveType.scrollStart, @@ -371,6 +342,7 @@ void main() { onObserve: (result) { observeCount++; }, + child: widget, ); await tester.pumpWidget(widget); final finder = find.byWidget(widget); @@ -399,20 +371,19 @@ void main() { testWidgets('Check isForbidObserveCallback', (tester) async { final scrollController = ScrollController(); - final observerController = - GridObserverController(controller: scrollController); - - Widget widget = getGridView( - scrollController: scrollController, + final observerController = GridObserverController( + controller: scrollController, ); + Widget widget = getGridView(scrollController: scrollController); + bool isCalledOnObserve = false; widget = GridViewObserver( - child: widget, controller: observerController, onObserve: (result) { isCalledOnObserve = true; }, + child: widget, ); await tester.pumpWidget(widget); @@ -437,21 +408,17 @@ void main() { controller: scrollController, ); - Widget widget = getGridView( - scrollController: scrollController, - ); + Widget widget = getGridView(scrollController: scrollController); GridViewObserveModel? observeResult; widget = GridViewObserver( - child: widget, controller: observerController, onObserve: (result) { observeResult = result; }, + child: widget, ); await tester.pumpWidget(widget); - await observerController.dispatchOnceObserve( - isForce: true, - ); + await observerController.dispatchOnceObserve(isForce: true); expect(observeResult, isNotNull); var firstGroupChildList = observeResult?.firstGroupChildList ?? []; expect(firstGroupChildList, isNotEmpty); @@ -465,10 +432,7 @@ void main() { ); int targetItemIndex = 30; - observerController.jumpTo( - index: targetItemIndex, - alignment: 0, - ); + observerController.jumpTo(index: targetItemIndex, alignment: 0); await tester.pumpAndSettle(); firstGroupChildList = observeResult?.firstGroupChildList ?? []; expect(firstGroupChildList, isNotEmpty); @@ -484,92 +448,76 @@ void main() { scrollController.dispose(); }); - group( - 'ObserverScrollNotification', - () { - late ScrollController scrollController; - late GridObserverController observerController; - late Widget widget; - - int indexOfStartNoti = -1; - int indexOfInterruptionNoti = -1; - int indexOfDecisionNoti = -1; - int indexOfEndNoti = -1; - - resetAll({ - bool isFixedHeight = false, - }) { - indexOfStartNoti = -1; - indexOfInterruptionNoti = -1; - indexOfDecisionNoti = -1; - indexOfEndNoti = -1; - scrollController = ScrollController(); - observerController = - GridObserverController(controller: scrollController); + group('ObserverScrollNotification', () { + late ScrollController scrollController; + late GridObserverController observerController; + late Widget widget; - widget = getGridView( - scrollController: scrollController, - itemCount: 100, - ); + int indexOfStartNoti = -1; + int indexOfInterruptionNoti = -1; + int indexOfDecisionNoti = -1; + int indexOfEndNoti = -1; - widget = GridViewObserver( - child: widget, - controller: observerController, - ); - int count = 0; - widget = NotificationListener( - child: widget, - onNotification: (notification) { - if (notification is ObserverScrollStartNotification) { - indexOfStartNoti = count; - } else if (notification is ObserverScrollInterruptionNotification) { - indexOfInterruptionNoti = count; - } else if (notification is ObserverScrollDecisionNotification) { - indexOfDecisionNoti = count; - } else if (notification is ObserverScrollEndNotification) { - indexOfEndNoti = count; - } - count += 1; - return true; - }, - ); - } + resetAll({bool isFixedHeight = false}) { + indexOfStartNoti = -1; + indexOfInterruptionNoti = -1; + indexOfDecisionNoti = -1; + indexOfEndNoti = -1; + scrollController = ScrollController(); + observerController = GridObserverController(controller: scrollController); - tearDown(() { - scrollController.dispose(); - }); - - testWidgets( - 'Notification sequence in normal scenarios', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - observerController.jumpTo(index: 10); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - expect(indexOfStartNoti, 0); - expect(indexOfInterruptionNoti, -1); - expect(indexOfDecisionNoti, 1); - expect(indexOfEndNoti, 2); - }, - ); + widget = getGridView(scrollController: scrollController, itemCount: 100); - testWidgets( - 'Notification sequence when using incorrect index', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - observerController.jumpTo(index: 101); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - expect(indexOfStartNoti, 0); - expect(indexOfInterruptionNoti, 1); - expect(indexOfDecisionNoti, -1); - expect(indexOfEndNoti, -1); + widget = GridViewObserver(controller: observerController, child: widget); + int count = 0; + widget = NotificationListener( + child: widget, + onNotification: (notification) { + if (notification is ObserverScrollStartNotification) { + indexOfStartNoti = count; + } else if (notification is ObserverScrollInterruptionNotification) { + indexOfInterruptionNoti = count; + } else if (notification is ObserverScrollDecisionNotification) { + indexOfDecisionNoti = count; + } else if (notification is ObserverScrollEndNotification) { + indexOfEndNoti = count; + } + count += 1; + return true; }, ); - }, - ); + } + + tearDown(() { + scrollController.dispose(); + }); + + testWidgets('Notification sequence in normal scenarios', (tester) async { + resetAll(); + await tester.pumpWidget(widget); + observerController.jumpTo(index: 10); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + expect(indexOfStartNoti, 0); + expect(indexOfInterruptionNoti, -1); + expect(indexOfDecisionNoti, 1); + expect(indexOfEndNoti, 2); + }); + + testWidgets('Notification sequence when using incorrect index', ( + tester, + ) async { + resetAll(); + await tester.pumpWidget(widget); + observerController.jumpTo(index: 101); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + expect(indexOfStartNoti, 0); + expect(indexOfInterruptionNoti, 1); + expect(indexOfDecisionNoti, -1); + expect(indexOfEndNoti, -1); + }); + }); group('dispatchOnceObserve', () { late ScrollController scrollController; @@ -582,90 +530,64 @@ void main() { resetAll() { scrollController = ScrollController(); - observerController = GridObserverController( - controller: scrollController, - ); - widget = getGridView( - scrollController: scrollController, - itemCount: 100, - ); - widget = GridViewObserver( - child: widget, - controller: observerController, - ); + observerController = GridObserverController(controller: scrollController); + widget = getGridView(scrollController: scrollController, itemCount: 100); + widget = GridViewObserver(controller: observerController, child: widget); } - testWidgets( - 'Check observeResult', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - var result = await observerController.dispatchOnceObserve(); - expect(result.isSuccess, isFalse); + testWidgets('Check observeResult', (tester) async { + resetAll(); + await tester.pumpWidget(widget); + var result = await observerController.dispatchOnceObserve(); + expect(result.isSuccess, isFalse); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect(result.observeResult, isNotNull); - expect( - result.observeResult?.displayingChildIndexList ?? [], - isNotEmpty, - ); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect(result.observeResult, isNotNull); + expect(result.observeResult?.displayingChildIndexList ?? [], isNotEmpty); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect( - result.observeResult?.displayingChildIndexList ?? [], - isEmpty, - ); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect(result.observeResult?.displayingChildIndexList ?? [], isEmpty); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - isForce: true, - ); - expect(result.isSuccess, isTrue); - expect( - result.observeResult?.displayingChildIndexList ?? [], - isNotEmpty, - ); - }, - ); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + isForce: true, + ); + expect(result.isSuccess, isTrue); + expect(result.observeResult?.displayingChildIndexList ?? [], isNotEmpty); + }); - testWidgets( - 'Check observeAllResult', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - var result = await observerController.dispatchOnceObserve(); - expect(result.isSuccess, isFalse); + testWidgets('Check observeAllResult', (tester) async { + resetAll(); + await tester.pumpWidget(widget); + var result = await observerController.dispatchOnceObserve(); + expect(result.isSuccess, isFalse); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect(result.observeAllResult.values.length, 1); - expect( - result.observeAllResult.values.first, - result.observeResult, - ); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect(result.observeAllResult.values.length, 1); + expect(result.observeAllResult.values.first, result.observeResult); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect(result.observeAllResult, isEmpty); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect(result.observeAllResult, isEmpty); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - isForce: true, - ); - expect(result.isSuccess, isTrue); - expect(result.observeAllResult, isNotEmpty); - }, - ); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + isForce: true, + ); + expect(result.isSuccess, isTrue); + expect(result.observeAllResult, isNotEmpty); + }); }); group('ObserverListener', () { @@ -682,9 +604,7 @@ void main() { scrollController.dispose(); }); - resetAll({ - bool isResetTag = false, - }) { + resetAll({bool isResetTag = false}) { if (isResetTag) { tag1 = tag1 * 2; tag2 = tag2 * 2; @@ -706,29 +626,25 @@ void main() { itemCount: 100, itemBuilder: (context, index) { if (index == 3) { - return const Center( - child: Icon(Icons.abc), - ); + return const Center(child: Icon(Icons.abc)); } return Container( color: Colors.blue, - child: Center( - child: Text('index -- $index'), - ), + child: Center(child: Text('index -- $index')), ); }, ); widget = GridViewObserver( key: key2, tag: tag2, - child: widget, controller: observerController2, + child: widget, ); widget = GridViewObserver( key: key1, tag: tag1, - child: widget, controller: observerController1, + child: widget, ); } @@ -750,9 +666,7 @@ void main() { resetAll(isResetTag: true); await tester.pumpWidget(widget); - tagManager = ObserverWidgetTagManager.maybeOf( - tester.element(itemFinder), - ); + tagManager = ObserverWidgetTagManager.maybeOf(tester.element(itemFinder)); tagMap = Map.from(tagManager?.tagMap ?? {}); Set newTags = {tag1, tag2}; expect(tags != newTags, isTrue); @@ -771,15 +685,11 @@ void main() { cbResult = result; } - onObserveAllCallback( - Map resultMap, - ) { + onObserveAllCallback(Map resultMap) { cbAllResult = resultMap; } - final observerState = GridViewObserver.of( - tester.element(itemFinder), - ); + final observerState = GridViewObserver.of(tester.element(itemFinder)); expect(observerState, isNotNull); final observerStateByTag2 = GridViewObserver.of( @@ -830,15 +740,11 @@ void main() { cbResult = result; } - onObserveAllCallback( - Map resultMap, - ) { + onObserveAllCallback(Map resultMap) { cbAllResult = resultMap; } - observerState = GridViewObserver.maybeOf( - tester.element(itemFinder), - ); + observerState = GridViewObserver.maybeOf(tester.element(itemFinder)); expect(observerState, isNotNull); final observerStateByTag2 = GridViewObserver.maybeOf( @@ -873,50 +779,43 @@ void main() { }); testWidgets( - 'innerTagChangeCount should not increase when tag remains unchanged', - (tester) async { - const String tag = 'tag1'; - scrollController = ScrollController(); - Widget gridView = getGridView(scrollController: scrollController); + 'innerTagChangeCount should not increase when tag remains unchanged', + (tester) async { + const String tag = 'tag1'; + scrollController = ScrollController(); + Widget gridView = getGridView(scrollController: scrollController); - widget = GridViewObserver( - tag: tag, - child: gridView, - ); + widget = GridViewObserver(tag: tag, child: gridView); - await tester.pumpWidget(widget); - await tester.pumpAndSettle(); + await tester.pumpWidget(widget); + await tester.pumpAndSettle(); - // Get ObserverWidgetState - final itemFinder = find.byType(GridViewObserver); - final observerState = tester.state(itemFinder); - expect(observerState, isNotNull); + // Get ObserverWidgetState + final itemFinder = find.byType(GridViewObserver); + final observerState = tester.state(itemFinder); + expect(observerState, isNotNull); - // Record initial tagChangeCount - final initialTagChangeCount = observerState.innerTagChangeCount; + // Record initial tagChangeCount + final initialTagChangeCount = observerState.innerTagChangeCount; - // Refresh widget but keep tag unchanged - widget = GridViewObserver( - tag: tag, - child: gridView, - ); - await tester.pumpWidget(widget); - await tester.pumpAndSettle(); + // Refresh widget but keep tag unchanged + widget = GridViewObserver(tag: tag, child: gridView); + await tester.pumpWidget(widget); + await tester.pumpAndSettle(); - // Verify that tagChangeCount has not increased - expect(observerState.innerTagChangeCount, initialTagChangeCount); - expect(observerState.innerTagChangeCount, 0); - }); + // Verify that tagChangeCount has not increased + expect(observerState.innerTagChangeCount, initialTagChangeCount); + expect(observerState.innerTagChangeCount, 0); + }, + ); - testWidgets('No exception in _checkTagChange during refresh and dispose', - (tester) async { + testWidgets('No exception in _checkTagChange during refresh and dispose', ( + tester, + ) async { // Regression test for https://github.com/fluttercandies/flutter_scrollview_observer/issues/143 scrollController = ScrollController(); Widget gridView = getGridView(scrollController: scrollController); - widget = GridViewObserver( - tag: 'tag1', - child: gridView, - ); + widget = GridViewObserver(tag: 'tag1', child: gridView); await tester.pumpWidget(widget); // Get ObserverWidgetState @@ -927,10 +826,7 @@ void main() { final completer = Completer(); observerState.innerCheckTagChangeEndOfFrame = completer.future; - widget = GridViewObserver( - tag: 'tag2', - child: gridView, - ); + widget = GridViewObserver(tag: 'tag2', child: gridView); await tester.pumpWidget(widget); // Dispose widget before endOfFrame completes @@ -946,8 +842,9 @@ void main() { }); group('cancelOnceObserveNotificationBubbling', () { - testWidgets('cancelOnceObserveNotificationBubbling is true (default)', - (tester) async { + testWidgets('cancelOnceObserveNotificationBubbling is true (default)', ( + tester, + ) async { final scrollController = ScrollController(); final observerController = GridObserverController( controller: scrollController, @@ -984,8 +881,9 @@ void main() { scrollController.dispose(); }); - testWidgets('cancelOnceObserveNotificationBubbling is false', - (tester) async { + testWidgets('cancelOnceObserveNotificationBubbling is false', ( + tester, + ) async { final scrollController = ScrollController(); final observerController = GridObserverController( controller: scrollController, diff --git a/test/list_observer_test.dart b/test/list_observer_test.dart index 7e16fd8..cea3824 100644 --- a/test/list_observer_test.dart +++ b/test/list_observer_test.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; import 'package:scrollview_observer/src/common/observer_widget_tag_manager.dart'; @@ -31,12 +31,11 @@ void main() { } return SizedBox( height: height, - child: Center( - child: Text("index -- $index"), - ), + child: Center(child: Text("index -- $index")), ); }, - separatorBuilder: separatorBuilder ?? + separatorBuilder: + separatorBuilder ?? (ctx, index) { return const SizedBox(height: 10); }, @@ -61,9 +60,7 @@ void main() { itemBuilder: (ctx, index) { return SizedBox( height: height, - child: Center( - child: Text("index -- $index"), - ), + child: Center(child: Text("index -- $index")), ); }, itemExtent: useItemExtentBuilder ? null : height, @@ -84,13 +81,8 @@ void main() { final observerController = ListObserverController( controller: scrollController, ); - Widget widget = getListView( - scrollController: scrollController, - ); - widget = ListViewObserver( - child: widget, - controller: observerController, - ); + Widget widget = getListView(scrollController: scrollController); + widget = ListViewObserver(controller: observerController, child: widget); await tester.pumpWidget(widget); expect(observerController.sliverContexts.length, 1); scrollController.dispose(); @@ -124,12 +116,12 @@ void main() { }, ); widget = ListViewObserver( - child: widget, controller: observerController, scrollNotificationPredicate: defaultScrollNotificationPredicate, onObserve: (result) { isCalledOnObserve = true; }, + child: widget, ); await tester.pumpWidget(widget); @@ -172,18 +164,19 @@ void main() { ); int? lastItemIndex; widget = ListViewObserver( - child: widget, controller: observerController, triggerOnObserveType: ObserverTriggerOnObserveType.directly, onObserve: (result) { lastItemIndex = result.displayingChildModelList.last.index; }, + child: widget, ); await tester.pumpWidget(widget); var result = await observerController.dispatchOnceObserve(); - final offset = result.observeResult?.viewport.offset - as ScrollPositionWithSingleContext; + final offset = + result.observeResult?.viewport.offset + as ScrollPositionWithSingleContext; final maxScrollExtent = offset.maxScrollExtent; scrollController.animateTo( maxScrollExtent * 1000, @@ -205,16 +198,14 @@ void main() { controller: scrollController, ); - Widget widget = getListView( - scrollController: scrollController, - ); + Widget widget = getListView(scrollController: scrollController); ListViewObserveModel? observeResult; widget = ListViewObserver( - child: widget, controller: observerController, onObserve: (result) { observeResult = result; }, + child: widget, ); await tester.pumpWidget(widget); @@ -249,19 +240,16 @@ void main() { ); ListViewObserveModel? observeResult; widget = ListViewObserver( - child: widget, controller: observerController, onObserve: (result) { observeResult = result; }, + child: widget, ); await tester.pumpWidget(widget); int targeItemIndex = 30; - observerController.jumpTo( - index: targeItemIndex, - isFixedHeight: true, - ); + observerController.jumpTo(index: targeItemIndex, isFixedHeight: true); await tester.pumpAndSettle(); await tester.pump(observerController.observeIntervalForScrolling); expect(observeResult?.firstChild?.index, targeItemIndex); @@ -282,8 +270,9 @@ void main() { testWidgets('Fixed height with itemExtentBuilder', (tester) async { final scrollController = ScrollController(); - final observerController = - ListObserverController(controller: scrollController); + final observerController = ListObserverController( + controller: scrollController, + ); Widget widget = getFixedHeightListView( scrollController: scrollController, @@ -291,19 +280,16 @@ void main() { ); ListViewObserveModel? observeResult; widget = ListViewObserver( - child: widget, controller: observerController, onObserve: (result) { observeResult = result; }, + child: widget, ); await tester.pumpWidget(widget); int targeItemIndex = 30; - observerController.jumpTo( - index: targeItemIndex, - isFixedHeight: true, - ); + observerController.jumpTo(index: targeItemIndex, isFixedHeight: true); await tester.pumpAndSettle(); await tester.pump(observerController.observeIntervalForScrolling); expect(observeResult?.firstChild?.index, targeItemIndex); @@ -324,51 +310,45 @@ void main() { // Regression test for https://github.com/fluttercandies/flutter_scrollview_observer/issues/123 testWidgets( - 'No exception when ListViewObserverState is disposed during scrolling', - (tester) async { - final scrollController = ScrollController(); - final observerController = ListObserverController( - controller: scrollController, - ); + 'No exception when ListViewObserverState is disposed during scrolling', + (tester) async { + final scrollController = ScrollController(); + final observerController = ListObserverController( + controller: scrollController, + ); - Widget widget = getListView( - scrollController: scrollController, - ); - widget = ListViewObserver( - child: widget, - controller: observerController, - onObserve: (_) {}, - ); - await tester.pumpWidget(widget); + Widget widget = getListView(scrollController: scrollController); + widget = ListViewObserver( + controller: observerController, + onObserve: (_) {}, + child: widget, + ); + await tester.pumpWidget(widget); - observerController.animateTo( - index: 10, - duration: const Duration(seconds: 3), - curve: Curves.easeInOut, - ); + observerController.animateTo( + index: 10, + duration: const Duration(seconds: 3), + curve: Curves.easeInOut, + ); - await tester.pump(); - await tester.pump(const Duration(seconds: 1)); - await tester.pumpWidget(Container()); + await tester.pump(); + await tester.pump(const Duration(seconds: 1)); + await tester.pumpWidget(Container()); - scrollController.dispose(); - }); + scrollController.dispose(); + }, + ); }); group('Cache index offset', () { testWidgets('Property cacheJumpIndexOffset', (tester) async { final scrollController = ScrollController(); - final observerController = - ListObserverController(controller: scrollController) - ..cacheJumpIndexOffset = false; + final observerController = ListObserverController( + controller: scrollController, + )..cacheJumpIndexOffset = false; - Widget widget = getListView( - scrollController: scrollController, - ); - widget = ListViewObserver( - child: widget, - controller: observerController, - ); + Widget widget = getListView(scrollController: scrollController); + widget = ListViewObserver(controller: observerController, child: widget); await tester.pumpWidget(widget); observerController.jumpTo(index: 30); @@ -381,16 +361,12 @@ void main() { testWidgets('Method clearScrollIndexCache', (tester) async { final scrollController = ScrollController(); - final observerController = - ListObserverController(controller: scrollController); - - Widget widget = getListView( - scrollController: scrollController, - ); - widget = ListViewObserver( - child: widget, - controller: observerController, + final observerController = ListObserverController( + controller: scrollController, ); + + Widget widget = getListView(scrollController: scrollController); + widget = ListViewObserver(controller: observerController, child: widget); await tester.pumpWidget(widget); final ctx = observerController.fetchSliverContext(); @@ -422,9 +398,9 @@ void main() { // Building the same widget for the same index on purpose, so that no // existing child needs to be laid out again after the rebuild. Widget buildItem(BuildContext ctx, int index) => const SizedBox( - height: itemHeight, - child: Center(child: Text('item')), - ); + height: itemHeight, + child: Center(child: Text('item')), + ); Future testJumpToTheLastIndex( WidgetTester tester, { @@ -442,29 +418,28 @@ void main() { var itemCount = 20; late StateSetter setStateFn; - Widget widget = StatefulBuilder(builder: (context, setState) { - setStateFn = setState; - return Directionality( - textDirection: TextDirection.ltr, - child: isSeparated - ? ListView.separated( - controller: scrollController, - itemBuilder: buildItem, - separatorBuilder: (ctx, index) => - const SizedBox(height: separatorHeight), - itemCount: itemCount, - ) - : ListView.builder( - controller: scrollController, - itemBuilder: buildItem, - itemCount: itemCount, - ), - ); - }); - widget = ListViewObserver( - child: widget, - controller: observerController, + Widget widget = StatefulBuilder( + builder: (context, setState) { + setStateFn = setState; + return Directionality( + textDirection: TextDirection.ltr, + child: isSeparated + ? ListView.separated( + controller: scrollController, + itemBuilder: buildItem, + separatorBuilder: (ctx, index) => + const SizedBox(height: separatorHeight), + itemCount: itemCount, + ) + : ListView.builder( + controller: scrollController, + itemBuilder: buildItem, + itemCount: itemCount, + ), + ); + }, ); + widget = ListViewObserver(controller: observerController, child: widget); await tester.pumpWidget(widget); await tester.pumpAndSettle(); @@ -510,24 +485,19 @@ void main() { controller: scrollController, ); - Widget widget = getListView( - scrollController: scrollController, - ); + Widget widget = getListView(scrollController: scrollController); ListViewObserveModel? observeResult; widget = ListViewObserver( - child: widget, controller: observerController, onObserve: (result) { observeResult = result; }, + child: widget, ); await tester.pumpWidget(widget); int targetItemIndex = 30; - observerController.jumpTo( - index: targetItemIndex, - alignment: 0, - ); + observerController.jumpTo(index: targetItemIndex, alignment: 0); await tester.pumpAndSettle(); await tester.pump(observerController.observeIntervalForScrolling); var firstChild = observeResult?.firstChild; @@ -535,20 +505,14 @@ void main() { expect(firstChild?.index, targetItemIndex); expect(firstChild?.displayPercentage, 1); - observerController.jumpTo( - index: targetItemIndex, - alignment: 0.5, - ); + observerController.jumpTo(index: targetItemIndex, alignment: 0.5); await tester.pumpAndSettle(); await tester.pump(observerController.observeIntervalForScrolling); firstChild = observeResult?.firstChild; expect(firstChild?.index, targetItemIndex); expect(firstChild?.displayPercentage, 0.5); - observerController.jumpTo( - index: targetItemIndex, - alignment: 1, - ); + observerController.jumpTo(index: targetItemIndex, alignment: 1); await tester.pumpAndSettle(); await tester.pump(observerController.observeIntervalForScrolling); firstChild = observeResult?.firstChild; @@ -564,11 +528,8 @@ void main() { )..observeIntervalForScrolling = const Duration(milliseconds: 500); int observeCount = 0; - Widget widget = getListView( - scrollController: scrollController, - ); + Widget widget = getListView(scrollController: scrollController); widget = ListViewObserver( - child: widget, controller: observerController, autoTriggerObserveTypes: const [ ObserverAutoTriggerObserveType.scrollStart, @@ -579,6 +540,7 @@ void main() { onObserve: (result) { observeCount++; }, + child: widget, ); await tester.pumpWidget(widget); final finder = find.byWidget(widget); @@ -607,20 +569,19 @@ void main() { testWidgets('Check isForbidObserveCallback', (tester) async { final scrollController = ScrollController(); - final observerController = - ListObserverController(controller: scrollController); - - Widget widget = getListView( - scrollController: scrollController, + final observerController = ListObserverController( + controller: scrollController, ); + Widget widget = getListView(scrollController: scrollController); + bool isCalledOnObserve = false; widget = ListViewObserver( - child: widget, controller: observerController, onObserve: (result) { isCalledOnObserve = true; }, + child: widget, ); await tester.pumpWidget(widget); @@ -645,21 +606,17 @@ void main() { controller: scrollController, ); - Widget widget = getListView( - scrollController: scrollController, - ); + Widget widget = getListView(scrollController: scrollController); ListViewObserveModel? observeResult; widget = ListViewObserver( - child: widget, controller: observerController, onObserve: (result) { observeResult = result; }, + child: widget, ); await tester.pumpWidget(widget); - await observerController.dispatchOnceObserve( - isForce: true, - ); + await observerController.dispatchOnceObserve(isForce: true); expect(observeResult, isNotNull); expect(observeResult?.firstChild?.index, 0); expect( @@ -671,10 +628,7 @@ void main() { ); int targetItemIndex = 30; - observerController.jumpTo( - index: targetItemIndex, - alignment: 0, - ); + observerController.jumpTo(index: targetItemIndex, alignment: 0); await tester.pumpAndSettle(); await tester.pump(observerController.observeIntervalForScrolling); expect(observeResult?.firstChild?.index, targetItemIndex); @@ -689,123 +643,109 @@ void main() { scrollController.dispose(); }); - group( - 'ObserverScrollNotification', - () { - late ScrollController scrollController; - late ListObserverController observerController; - late Widget widget; - - int indexOfStartNoti = -1; - int indexOfInterruptionNoti = -1; - int indexOfDecisionNoti = -1; - int indexOfEndNoti = -1; - - resetAll({ - bool isFixedHeight = false, - }) { - indexOfStartNoti = -1; - indexOfInterruptionNoti = -1; - indexOfDecisionNoti = -1; - indexOfEndNoti = -1; - scrollController = ScrollController(); - observerController = ListObserverController( - controller: scrollController, - ); + group('ObserverScrollNotification', () { + late ScrollController scrollController; + late ListObserverController observerController; + late Widget widget; - widget = getListView( - scrollController: scrollController, - itemCount: 100, - isFixedHeight: isFixedHeight, - ); + int indexOfStartNoti = -1; + int indexOfInterruptionNoti = -1; + int indexOfDecisionNoti = -1; + int indexOfEndNoti = -1; - widget = ListViewObserver( - child: widget, - controller: observerController, - ); - int count = 0; - widget = NotificationListener( - child: widget, - onNotification: (notification) { - if (notification is ObserverScrollStartNotification) { - indexOfStartNoti = count; - } else if (notification is ObserverScrollInterruptionNotification) { - indexOfInterruptionNoti = count; - } else if (notification is ObserverScrollDecisionNotification) { - indexOfDecisionNoti = count; - } else if (notification is ObserverScrollEndNotification) { - indexOfEndNoti = count; - } - count += 1; - return true; - }, - ); - } + resetAll({bool isFixedHeight = false}) { + indexOfStartNoti = -1; + indexOfInterruptionNoti = -1; + indexOfDecisionNoti = -1; + indexOfEndNoti = -1; + scrollController = ScrollController(); + observerController = ListObserverController(controller: scrollController); - tearDown(() { - scrollController.dispose(); - }); - - testWidgets( - 'Notification sequence in normal scenarios', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - observerController.jumpTo(index: 10); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - expect(indexOfStartNoti, 0); - expect(indexOfInterruptionNoti, -1); - expect(indexOfDecisionNoti, 1); - expect(indexOfEndNoti, 2); - }, + widget = getListView( + scrollController: scrollController, + itemCount: 100, + isFixedHeight: isFixedHeight, ); - testWidgets( - 'Notification sequence in normal scenarios with fixed item height', - (tester) async { - resetAll(isFixedHeight: true); - await tester.pumpWidget(widget); - observerController.jumpTo(index: 10); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - expect(indexOfStartNoti, 0); - expect(indexOfInterruptionNoti, -1); - expect(indexOfDecisionNoti, 1); - expect(indexOfEndNoti, 2); + widget = ListViewObserver(controller: observerController, child: widget); + int count = 0; + widget = NotificationListener( + child: widget, + onNotification: (notification) { + if (notification is ObserverScrollStartNotification) { + indexOfStartNoti = count; + } else if (notification is ObserverScrollInterruptionNotification) { + indexOfInterruptionNoti = count; + } else if (notification is ObserverScrollDecisionNotification) { + indexOfDecisionNoti = count; + } else if (notification is ObserverScrollEndNotification) { + indexOfEndNoti = count; + } + count += 1; + return true; }, ); + } - testWidgets( - 'Notification sequence when using incorrect index', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - observerController.jumpTo(index: 101); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - expect(indexOfStartNoti, 0); - expect(indexOfInterruptionNoti, 1); - expect(indexOfDecisionNoti, -1); - expect(indexOfEndNoti, -1); - }, - ); + tearDown(() { + scrollController.dispose(); + }); - testWidgets( - 'Notification sequence when using incorrect index with fixed item height', - (tester) async { - resetAll(isFixedHeight: true); - await tester.pumpWidget(widget); - observerController.jumpTo(index: 101); - await tester.pumpAndSettle(); - expect(indexOfStartNoti, 0); - expect(indexOfInterruptionNoti, 1); - expect(indexOfDecisionNoti, -1); - expect(indexOfEndNoti, -1); - }, - ); - }, - ); + testWidgets('Notification sequence in normal scenarios', (tester) async { + resetAll(); + await tester.pumpWidget(widget); + observerController.jumpTo(index: 10); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + expect(indexOfStartNoti, 0); + expect(indexOfInterruptionNoti, -1); + expect(indexOfDecisionNoti, 1); + expect(indexOfEndNoti, 2); + }); + + testWidgets( + 'Notification sequence in normal scenarios with fixed item height', + (tester) async { + resetAll(isFixedHeight: true); + await tester.pumpWidget(widget); + observerController.jumpTo(index: 10); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + expect(indexOfStartNoti, 0); + expect(indexOfInterruptionNoti, -1); + expect(indexOfDecisionNoti, 1); + expect(indexOfEndNoti, 2); + }, + ); + + testWidgets('Notification sequence when using incorrect index', ( + tester, + ) async { + resetAll(); + await tester.pumpWidget(widget); + observerController.jumpTo(index: 101); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + expect(indexOfStartNoti, 0); + expect(indexOfInterruptionNoti, 1); + expect(indexOfDecisionNoti, -1); + expect(indexOfEndNoti, -1); + }); + + testWidgets( + 'Notification sequence when using incorrect index with fixed item height', + (tester) async { + resetAll(isFixedHeight: true); + await tester.pumpWidget(widget); + observerController.jumpTo(index: 101); + await tester.pumpAndSettle(); + expect(indexOfStartNoti, 0); + expect(indexOfInterruptionNoti, 1); + expect(indexOfDecisionNoti, -1); + expect(indexOfEndNoti, -1); + }, + ); + }); group('dispatchOnceObserve', () { late ScrollController scrollController; @@ -818,90 +758,64 @@ void main() { resetAll() { scrollController = ScrollController(); - observerController = ListObserverController( - controller: scrollController, - ); - widget = getListView( - scrollController: scrollController, - itemCount: 100, - ); - widget = ListViewObserver( - child: widget, - controller: observerController, - ); + observerController = ListObserverController(controller: scrollController); + widget = getListView(scrollController: scrollController, itemCount: 100); + widget = ListViewObserver(controller: observerController, child: widget); } - testWidgets( - 'Check observeResult', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - var result = await observerController.dispatchOnceObserve(); - expect(result.isSuccess, isFalse); + testWidgets('Check observeResult', (tester) async { + resetAll(); + await tester.pumpWidget(widget); + var result = await observerController.dispatchOnceObserve(); + expect(result.isSuccess, isFalse); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect(result.observeResult, isNotNull); - expect( - result.observeResult?.displayingChildIndexList ?? [], - isNotEmpty, - ); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect(result.observeResult, isNotNull); + expect(result.observeResult?.displayingChildIndexList ?? [], isNotEmpty); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect( - result.observeResult?.displayingChildIndexList ?? [], - isEmpty, - ); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect(result.observeResult?.displayingChildIndexList ?? [], isEmpty); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - isForce: true, - ); - expect(result.isSuccess, isTrue); - expect( - result.observeResult?.displayingChildIndexList ?? [], - isNotEmpty, - ); - }, - ); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + isForce: true, + ); + expect(result.isSuccess, isTrue); + expect(result.observeResult?.displayingChildIndexList ?? [], isNotEmpty); + }); - testWidgets( - 'Check observeAllResult', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - var result = await observerController.dispatchOnceObserve(); - expect(result.isSuccess, isFalse); + testWidgets('Check observeAllResult', (tester) async { + resetAll(); + await tester.pumpWidget(widget); + var result = await observerController.dispatchOnceObserve(); + expect(result.isSuccess, isFalse); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect(result.observeAllResult.values.length, 1); - expect( - result.observeAllResult.values.first, - result.observeResult, - ); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect(result.observeAllResult.values.length, 1); + expect(result.observeAllResult.values.first, result.observeResult); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect(result.observeAllResult, isEmpty); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect(result.observeAllResult, isEmpty); - result = await observerController.dispatchOnceObserve( - isDependObserveCallback: false, - isForce: true, - ); - expect(result.isSuccess, isTrue); - expect(result.observeAllResult, isNotEmpty); - }, - ); + result = await observerController.dispatchOnceObserve( + isDependObserveCallback: false, + isForce: true, + ); + expect(result.isSuccess, isTrue); + expect(result.observeAllResult, isNotEmpty); + }); }); group('ObserverListener', () { @@ -918,9 +832,7 @@ void main() { scrollController.dispose(); }); - resetAll({ - bool isResetTag = false, - }) { + resetAll({bool isResetTag = false}) { if (isResetTag) { tag1 = tag1 * 2; tag2 = tag2 * 2; @@ -944,30 +856,26 @@ void main() { if (index == 3) { return const SizedBox( height: 50, - child: Center( - child: Icon(Icons.abc), - ), + child: Center(child: Icon(Icons.abc)), ); } return SizedBox( height: 80, - child: Center( - child: Text("index -- $index"), - ), + child: Center(child: Text("index -- $index")), ); }, ); widget = ListViewObserver( key: key2, tag: tag2, - child: widget, controller: observerController2, + child: widget, ); widget = ListViewObserver( key: key1, tag: tag1, - child: widget, controller: observerController1, + child: widget, ); } @@ -989,9 +897,7 @@ void main() { resetAll(isResetTag: true); await tester.pumpWidget(widget); - tagManager = ObserverWidgetTagManager.maybeOf( - tester.element(itemFinder), - ); + tagManager = ObserverWidgetTagManager.maybeOf(tester.element(itemFinder)); tagMap = Map.from(tagManager?.tagMap ?? {}); Set newTags = {tag1, tag2}; expect(tags != newTags, isTrue); @@ -1010,15 +916,11 @@ void main() { cbResult = result; } - onObserveAllCallback( - Map resultMap, - ) { + onObserveAllCallback(Map resultMap) { cbAllResult = resultMap; } - final observerState = ListViewObserver.of( - tester.element(itemFinder), - ); + final observerState = ListViewObserver.of(tester.element(itemFinder)); expect(observerState, isNotNull); final observerStateByTag2 = ListViewObserver.of( @@ -1068,15 +970,11 @@ void main() { cbResult = result; } - onObserveAllCallback( - Map resultMap, - ) { + onObserveAllCallback(Map resultMap) { cbAllResult = resultMap; } - observerState = ListViewObserver.maybeOf( - tester.element(itemFinder), - ); + observerState = ListViewObserver.maybeOf(tester.element(itemFinder)); expect(observerState, isNotNull); final observerStateByTag2 = ListViewObserver.maybeOf( @@ -1111,50 +1009,43 @@ void main() { }); testWidgets( - 'innerTagChangeCount should not increase when tag remains unchanged', - (tester) async { - const String tag = 'tag1'; - scrollController = ScrollController(); - Widget listView = getListView(scrollController: scrollController); + 'innerTagChangeCount should not increase when tag remains unchanged', + (tester) async { + const String tag = 'tag1'; + scrollController = ScrollController(); + Widget listView = getListView(scrollController: scrollController); - widget = ListViewObserver( - tag: tag, - child: listView, - ); + widget = ListViewObserver(tag: tag, child: listView); - await tester.pumpWidget(widget); - await tester.pumpAndSettle(); + await tester.pumpWidget(widget); + await tester.pumpAndSettle(); - // Get ObserverWidgetState - final itemFinder = find.byType(ListViewObserver); - final observerState = tester.state(itemFinder); - expect(observerState, isNotNull); + // Get ObserverWidgetState + final itemFinder = find.byType(ListViewObserver); + final observerState = tester.state(itemFinder); + expect(observerState, isNotNull); - // Record initial tagChangeCount - final initialTagChangeCount = observerState.innerTagChangeCount; + // Record initial tagChangeCount + final initialTagChangeCount = observerState.innerTagChangeCount; - // Refresh widget but keep tag unchanged - widget = ListViewObserver( - tag: tag, - child: listView, - ); - await tester.pumpWidget(widget); - await tester.pumpAndSettle(); + // Refresh widget but keep tag unchanged + widget = ListViewObserver(tag: tag, child: listView); + await tester.pumpWidget(widget); + await tester.pumpAndSettle(); - // Verify that tagChangeCount has not increased - expect(observerState.innerTagChangeCount, initialTagChangeCount); - expect(observerState.innerTagChangeCount, 0); - }); + // Verify that tagChangeCount has not increased + expect(observerState.innerTagChangeCount, initialTagChangeCount); + expect(observerState.innerTagChangeCount, 0); + }, + ); - testWidgets('No exception in _checkTagChange during refresh and dispose', - (tester) async { + testWidgets('No exception in _checkTagChange during refresh and dispose', ( + tester, + ) async { // Regression test for https://github.com/fluttercandies/flutter_scrollview_observer/issues/143 scrollController = ScrollController(); Widget listView = getListView(scrollController: scrollController); - widget = ListViewObserver( - tag: 'tag1', - child: listView, - ); + widget = ListViewObserver(tag: 'tag1', child: listView); await tester.pumpWidget(widget); // Get ObserverWidgetState @@ -1165,10 +1056,7 @@ void main() { final completer = Completer(); observerState.innerCheckTagChangeEndOfFrame = completer.future; - widget = ListViewObserver( - tag: 'tag2', - child: listView, - ); + widget = ListViewObserver(tag: 'tag2', child: listView); await tester.pumpWidget(widget); // Dispose widget before endOfFrame completes @@ -1184,8 +1072,9 @@ void main() { }); group('cancelOnceObserveNotificationBubbling', () { - testWidgets('cancelOnceObserveNotificationBubbling is true (default)', - (tester) async { + testWidgets('cancelOnceObserveNotificationBubbling is true (default)', ( + tester, + ) async { final scrollController = ScrollController(); final observerController = ListObserverController( controller: scrollController, @@ -1219,8 +1108,9 @@ void main() { scrollController.dispose(); }); - testWidgets('cancelOnceObserveNotificationBubbling is false', - (tester) async { + testWidgets('cancelOnceObserveNotificationBubbling is false', ( + tester, + ) async { final scrollController = ScrollController(); final observerController = ListObserverController( controller: scrollController, diff --git a/test/observer_utils_test.dart b/test/observer_utils_test.dart index 8391b58..948fc5a 100644 --- a/test/observer_utils_test.dart +++ b/test/observer_utils_test.dart @@ -4,7 +4,7 @@ * @Date: 2024-05-20 22:19:27 */ -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; @@ -20,10 +20,7 @@ void main() { child: ListView.separated( controller: scrollController, itemBuilder: (ctx, index) { - return const SizedBox( - width: double.infinity, - height: 80, - ); + return const SizedBox(width: double.infinity, height: 80); }, separatorBuilder: (ctx, index) { return const SizedBox(height: 10); @@ -32,9 +29,9 @@ void main() { ), ); widget = ListViewObserver( - child: widget, controller: observerController, onObserve: (resultModel) => observeModel = resultModel, + child: widget, ); await tester.pumpWidget(widget); diff --git a/test/sliver_observer_test.dart b/test/sliver_observer_test.dart index af147ba..da29d8f 100644 --- a/test/sliver_observer_test.dart +++ b/test/sliver_observer_test.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:scrollview_observer/scrollview_observer.dart'; @@ -9,8 +9,8 @@ import 'package:scrollview_observer/src/common/observer_widget_tag_manager.dart' void main() { GlobalKey sliverListKey = GlobalKey(); GlobalKey sliverGridKey = GlobalKey(); - BuildContext? _sliverListCtx; - BuildContext? _sliverGridCtx; + BuildContext? sliverListCtx; + BuildContext? sliverGridCtx; double calcPersistentHeaderExtent({ required double offset, @@ -22,42 +22,28 @@ void main() { ); } - Widget _buildDirectionality({ - required Widget child, - }) { - return Directionality( - textDirection: TextDirection.ltr, - child: child, - ); + Widget buildDirectionality({required Widget child}) { + return Directionality(textDirection: TextDirection.ltr, child: child); } - Widget _buildSliverListView({ - NullableIndexedWidgetBuilder? builder, - }) { + Widget buildSliverListView({NullableIndexedWidgetBuilder? builder}) { return SliverList( key: sliverListKey, - delegate: SliverChildBuilderDelegate( - (ctx, index) { - _sliverListCtx ??= ctx; - if (builder != null) { - return builder(ctx, index); - } - return Container( - height: (index % 2 == 0) ? 80 : 50, - color: Colors.red, - child: Center( - child: Text("index -- $index"), - ), - ); - }, - childCount: 30, - ), + delegate: SliverChildBuilderDelegate((ctx, index) { + sliverListCtx ??= ctx; + if (builder != null) { + return builder(ctx, index); + } + return Container( + height: (index % 2 == 0) ? 80 : 50, + color: Colors.red, + child: Center(child: Text("index -- $index")), + ); + }, childCount: 30), ); } - Widget _buildSliverGridView({ - NullableIndexedWidgetBuilder? builder, - }) { + Widget buildSliverGridView({NullableIndexedWidgetBuilder? builder}) { return SliverGrid( key: sliverGridKey, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( @@ -66,47 +52,38 @@ void main() { crossAxisSpacing: 10.0, childAspectRatio: 2.0, ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - _sliverGridCtx ??= context; - if (builder != null) { - return builder(context, index); - } - return Container( - color: Colors.green, - child: Center( - child: Text('index -- $index'), - ), - ); - }, - childCount: 150, - ), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + sliverGridCtx ??= context; + if (builder != null) { + return builder(context, index); + } + return Container( + color: Colors.green, + child: Center(child: Text('index -- $index')), + ); + }, childCount: 150), ); } - Widget _buildScrollView({ + Widget buildScrollView({ ScrollController? scrollController, NullableIndexedWidgetBuilder? listItemBuilder, NullableIndexedWidgetBuilder? gridItemBuilder, }) { - return _buildDirectionality( + return buildDirectionality( child: CustomScrollView( controller: scrollController, slivers: [ - _buildSliverListView( - builder: listItemBuilder, - ), - _buildSliverGridView( - builder: gridItemBuilder, - ), + buildSliverListView(builder: listItemBuilder), + buildSliverGridView(builder: gridItemBuilder), ], ), ); } tearDown(() { - _sliverListCtx = null; - _sliverGridCtx = null; + sliverListCtx = null; + sliverGridCtx = null; }); testWidgets('Check isForbidObserveCallback', (tester) async { @@ -115,34 +92,32 @@ void main() { controller: scrollController, ); - Widget widget = _buildScrollView( - scrollController: scrollController, - ); + Widget widget = buildScrollView(scrollController: scrollController); bool isCalledOnObserve = false; widget = SliverViewObserver( - child: widget, controller: observerController, sliverContexts: () { return [ - if (_sliverListCtx != null) _sliverListCtx!, - if (_sliverGridCtx != null) _sliverGridCtx!, + if (sliverListCtx != null) sliverListCtx!, + if (sliverGridCtx != null) sliverGridCtx!, ]; }, onObserveAll: (result) { isCalledOnObserve = true; }, + child: widget, ); await tester.pumpWidget(widget); observerController.isForbidObserveCallback = true; - observerController.jumpTo(index: 10, sliverContext: _sliverListCtx); + observerController.jumpTo(index: 10, sliverContext: sliverListCtx); await tester.pumpAndSettle(); await tester.pump(observerController.observeIntervalForScrolling); expect(isCalledOnObserve, false); observerController.isForbidObserveCallback = false; - observerController.jumpTo(index: 20, sliverContext: _sliverListCtx); + observerController.jumpTo(index: 20, sliverContext: sliverListCtx); await tester.pumpAndSettle(); await tester.pump(observerController.observeIntervalForScrolling); expect(isCalledOnObserve, true); @@ -156,13 +131,10 @@ void main() { controller: scrollController, ); - Widget widget = _buildScrollView( - scrollController: scrollController, - ); + Widget widget = buildScrollView(scrollController: scrollController); bool isCalledOnObserveViewport = false; widget = SliverViewObserver( - child: widget, controller: observerController, sliverContexts: () { return [ @@ -175,17 +147,18 @@ void main() { onObserveViewport: (result) { isCalledOnObserveViewport = true; }, + child: widget, ); await tester.pumpWidget(widget); observerController.isForbidObserveViewportCallback = true; - observerController.jumpTo(index: 10, sliverContext: _sliverListCtx); + observerController.jumpTo(index: 10, sliverContext: sliverListCtx); await tester.pumpAndSettle(); await tester.pump(observerController.observeIntervalForScrolling); expect(isCalledOnObserveViewport, false); observerController.isForbidObserveViewportCallback = false; - observerController.jumpTo(index: 20, sliverContext: _sliverGridCtx); + observerController.jumpTo(index: 20, sliverContext: sliverGridCtx); await tester.pumpAndSettle(); await tester.pump(observerController.observeIntervalForScrolling); expect(isCalledOnObserveViewport, true); @@ -201,12 +174,9 @@ void main() { int observeCountForOnObserveAll = 0; int observeCountForOnObserveViewport = 0; - Widget widget = _buildScrollView( - scrollController: scrollController, - ); + Widget widget = buildScrollView(scrollController: scrollController); widget = SliverViewObserver( - child: widget, controller: observerController, sliverContexts: () { return [ @@ -228,6 +198,7 @@ void main() { onObserveViewport: (_) { observeCountForOnObserveViewport++; }, + child: widget, ); await tester.pumpWidget(widget); final finder = find.byWidget(widget); @@ -265,7 +236,7 @@ void main() { final pageController = PageController(); bool isCalledOnObserve = false; - Widget widget = _buildScrollView( + Widget widget = buildScrollView( scrollController: scrollController, listItemBuilder: (context, index) { if (index == 0) { @@ -285,18 +256,18 @@ void main() { }, ); widget = SliverViewObserver( - child: widget, controller: observerController, scrollNotificationPredicate: defaultScrollNotificationPredicate, sliverContexts: () { return [ - if (_sliverListCtx != null) _sliverListCtx!, - if (_sliverGridCtx != null) _sliverGridCtx!, + if (sliverListCtx != null) sliverListCtx!, + if (sliverGridCtx != null) sliverGridCtx!, ]; }, onObserveAll: (result) { isCalledOnObserve = true; }, + child: widget, ); await tester.pumpWidget(widget); @@ -323,92 +294,81 @@ void main() { pageController.dispose(); }); - group( - 'ObserverScrollNotification', - () { - late ScrollController scrollController; - late SliverObserverController observerController; - late Widget widget; - - int indexOfStartNoti = -1; - int indexOfInterruptionNoti = -1; - int indexOfDecisionNoti = -1; - int indexOfEndNoti = -1; - - resetAll({ - bool isFixedHeight = false, - }) { - indexOfStartNoti = -1; - indexOfInterruptionNoti = -1; - indexOfDecisionNoti = -1; - indexOfEndNoti = -1; - scrollController = ScrollController(); - observerController = SliverObserverController( - controller: scrollController, - ); + group('ObserverScrollNotification', () { + late ScrollController scrollController; + late SliverObserverController observerController; + late Widget widget; - widget = _buildScrollView( - scrollController: scrollController, - ); + int indexOfStartNoti = -1; + int indexOfInterruptionNoti = -1; + int indexOfDecisionNoti = -1; + int indexOfEndNoti = -1; - widget = SliverViewObserver( - child: widget, - controller: observerController, - ); - int count = 0; - widget = NotificationListener( - child: widget, - onNotification: (notification) { - if (notification is ObserverScrollStartNotification) { - indexOfStartNoti = count; - } else if (notification is ObserverScrollInterruptionNotification) { - indexOfInterruptionNoti = count; - } else if (notification is ObserverScrollDecisionNotification) { - indexOfDecisionNoti = count; - } else if (notification is ObserverScrollEndNotification) { - indexOfEndNoti = count; - } - count += 1; - return true; - }, - ); - } + resetAll({bool isFixedHeight = false}) { + indexOfStartNoti = -1; + indexOfInterruptionNoti = -1; + indexOfDecisionNoti = -1; + indexOfEndNoti = -1; + scrollController = ScrollController(); + observerController = SliverObserverController( + controller: scrollController, + ); - tearDown(() { - scrollController.dispose(); - }); + widget = buildScrollView(scrollController: scrollController); - testWidgets( - 'Notification sequence in normal scenarios', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - observerController.jumpTo(index: 10); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - expect(indexOfStartNoti, 0); - expect(indexOfInterruptionNoti, -1); - expect(indexOfDecisionNoti, 1); - expect(indexOfEndNoti, 2); - }, + widget = SliverViewObserver( + controller: observerController, + child: widget, ); - - testWidgets( - 'Notification sequence when using incorrect index', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - observerController.jumpTo(index: 101); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - expect(indexOfStartNoti, 0); - expect(indexOfInterruptionNoti, 1); - expect(indexOfDecisionNoti, -1); - expect(indexOfEndNoti, -1); + int count = 0; + widget = NotificationListener( + child: widget, + onNotification: (notification) { + if (notification is ObserverScrollStartNotification) { + indexOfStartNoti = count; + } else if (notification is ObserverScrollInterruptionNotification) { + indexOfInterruptionNoti = count; + } else if (notification is ObserverScrollDecisionNotification) { + indexOfDecisionNoti = count; + } else if (notification is ObserverScrollEndNotification) { + indexOfEndNoti = count; + } + count += 1; + return true; }, ); - }, - ); + } + + tearDown(() { + scrollController.dispose(); + }); + + testWidgets('Notification sequence in normal scenarios', (tester) async { + resetAll(); + await tester.pumpWidget(widget); + observerController.jumpTo(index: 10); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + expect(indexOfStartNoti, 0); + expect(indexOfInterruptionNoti, -1); + expect(indexOfDecisionNoti, 1); + expect(indexOfEndNoti, 2); + }); + + testWidgets('Notification sequence when using incorrect index', ( + tester, + ) async { + resetAll(); + await tester.pumpWidget(widget); + observerController.jumpTo(index: 101); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + expect(indexOfStartNoti, 0); + expect(indexOfInterruptionNoti, 1); + expect(indexOfDecisionNoti, -1); + expect(indexOfEndNoti, -1); + }); + }); group('dispatchOnceObserve', () { late ScrollController scrollController; @@ -424,243 +384,215 @@ void main() { observerController = SliverObserverController( controller: scrollController, ); - widget = _buildScrollView( - scrollController: scrollController, - ); + widget = buildScrollView(scrollController: scrollController); widget = SliverViewObserver( sliverContexts: () => [ - if (_sliverListCtx != null) _sliverListCtx!, - if (_sliverGridCtx != null) _sliverGridCtx! + if (sliverListCtx != null) sliverListCtx!, + if (sliverGridCtx != null) sliverGridCtx!, ], - child: widget, controller: observerController, + child: widget, ); } - testWidgets( - 'Check observeAllResult', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - var result = await observerController.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - ); - expect(result.isSuccess, isFalse); + testWidgets('Check observeAllResult', (tester) async { + resetAll(); + await tester.pumpWidget(widget); + var result = await observerController.dispatchOnceObserve( + sliverContext: sliverListCtx!, + ); + expect(result.isSuccess, isFalse); - result = await observerController.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect(result.observeAllResult[_sliverListCtx], isNotNull); - expect( - result.observeAllResult[_sliverListCtx]?.displayingChildIndexList ?? - [], - isNotEmpty, - ); - expect(result.observeAllResult[_sliverGridCtx], isNotNull); + result = await observerController.dispatchOnceObserve( + sliverContext: sliverListCtx!, + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect(result.observeAllResult[sliverListCtx], isNotNull); + expect( + result.observeAllResult[sliverListCtx]?.displayingChildIndexList ?? [], + isNotEmpty, + ); + expect(result.observeAllResult[sliverGridCtx], isNotNull); - result = await observerController.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect( - result.observeAllResult[_sliverListCtx]?.displayingChildIndexList ?? - [], - isEmpty, - ); + result = await observerController.dispatchOnceObserve( + sliverContext: sliverListCtx!, + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect( + result.observeAllResult[sliverListCtx]?.displayingChildIndexList ?? [], + isEmpty, + ); - result = await observerController.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - isDependObserveCallback: false, - isForce: true, - ); - expect(result.isSuccess, isTrue); - expect( - result.observeAllResult[_sliverListCtx]?.displayingChildIndexList ?? - [], - isNotEmpty, - ); - }, - ); + result = await observerController.dispatchOnceObserve( + sliverContext: sliverListCtx!, + isDependObserveCallback: false, + isForce: true, + ); + expect(result.isSuccess, isTrue); + expect( + result.observeAllResult[sliverListCtx]?.displayingChildIndexList ?? [], + isNotEmpty, + ); + }); - testWidgets( - 'Check observeViewportResultModel', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - var result = await observerController.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - ); - expect(result.isSuccess, isFalse); - result = await observerController.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect( - result.observeViewportResultModel?.firstChild.sliverContext, - _sliverListCtx, - ); - expect( - result.observeViewportResultModel?.displayingChildModelList ?? [], - isNotEmpty, - ); - result = await observerController.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect( - result.observeViewportResultModel?.displayingChildModelList ?? [], - isEmpty, - ); - result = await observerController.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - isDependObserveCallback: false, - isForce: true, - ); - expect(result.isSuccess, isTrue); - expect( - result.observeViewportResultModel?.displayingChildModelList ?? [], - isNotEmpty, - ); + testWidgets('Check observeViewportResultModel', (tester) async { + resetAll(); + await tester.pumpWidget(widget); + var result = await observerController.dispatchOnceObserve( + sliverContext: sliverListCtx!, + ); + expect(result.isSuccess, isFalse); + result = await observerController.dispatchOnceObserve( + sliverContext: sliverListCtx!, + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect( + result.observeViewportResultModel?.firstChild.sliverContext, + sliverListCtx, + ); + expect( + result.observeViewportResultModel?.displayingChildModelList ?? [], + isNotEmpty, + ); + result = await observerController.dispatchOnceObserve( + sliverContext: sliverListCtx!, + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect( + result.observeViewportResultModel?.displayingChildModelList ?? [], + isEmpty, + ); + result = await observerController.dispatchOnceObserve( + sliverContext: sliverListCtx!, + isDependObserveCallback: false, + isForce: true, + ); + expect(result.isSuccess, isTrue); + expect( + result.observeViewportResultModel?.displayingChildModelList ?? [], + isNotEmpty, + ); - expect(_sliverGridCtx, isNotNull); - observerController.jumpTo( - index: 0, - sliverContext: _sliverGridCtx, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - result = await observerController.dispatchOnceObserve( - sliverContext: _sliverGridCtx!, - isDependObserveCallback: false, - ); - expect(result.isSuccess, isTrue); - expect( - result.observeViewportResultModel?.firstChild.sliverContext, - _sliverGridCtx, - ); - }, - ); + expect(sliverGridCtx, isNotNull); + observerController.jumpTo(index: 0, sliverContext: sliverGridCtx); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + result = await observerController.dispatchOnceObserve( + sliverContext: sliverGridCtx!, + isDependObserveCallback: false, + ); + expect(result.isSuccess, isTrue); + expect( + result.observeViewportResultModel?.firstChild.sliverContext, + sliverGridCtx, + ); + }); }); - group( - 'NestedScrollView', - () { - late ScrollController outerScrollController; - ScrollController? bodyScrollController; - late SliverObserverController observerController; - late Widget widget; - BuildContext? _sliverHeaderListCtx; - BuildContext? _sliverHeaderGridCtx; - BuildContext? _sliverBodyListCtx; - BuildContext? _sliverBodyGridCtx; - GlobalKey appBarKey = GlobalKey(); - GlobalKey nestedScrollViewKey = GlobalKey(); - NestedScrollUtil? nestedScrollUtil; - Map resultMap = {}; - int sliverListItemCount = 30; - int sliverGridItemCount = 150; - - Widget _buildSliverGridView() { - return SliverGrid( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - mainAxisSpacing: 10.0, - crossAxisSpacing: 10.0, - childAspectRatio: 2.0, - ), - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - if (_sliverBodyGridCtx != context) { - _sliverBodyGridCtx = context; - nestedScrollUtil?.bodySliverContexts.add(context); - } - return Container( - color: Colors.green, - child: Center( - child: Text('index -- $index'), - ), - ); - }, - childCount: sliverGridItemCount, - ), - ); - } - - Widget _buildSliverListView() { - return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (_sliverBodyListCtx != ctx) { - _sliverBodyListCtx = ctx; - nestedScrollUtil?.bodySliverContexts.add(ctx); - } - return Container( - height: (index % 2 == 0) ? 80 : 50, - color: Colors.red, - child: Center( - child: Text( - "index -- $index", - style: const TextStyle(color: Colors.white), - ), - ), - ); - }, - childCount: sliverListItemCount, - ), - ); - } + group('NestedScrollView', () { + late ScrollController outerScrollController; + ScrollController? bodyScrollController; + late SliverObserverController observerController; + late Widget widget; + BuildContext? sliverHeaderListCtx; + BuildContext? sliverHeaderGridCtx; + BuildContext? sliverBodyListCtx; + BuildContext? sliverBodyGridCtx; + GlobalKey appBarKey = GlobalKey(); + GlobalKey nestedScrollViewKey = GlobalKey(); + NestedScrollUtil? nestedScrollUtil; + Map resultMap = {}; + int sliverListItemCount = 30; + int sliverGridItemCount = 150; + + Widget buildSliverGridView() { + return SliverGrid( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 10.0, + crossAxisSpacing: 10.0, + childAspectRatio: 2.0, + ), + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + if (sliverBodyGridCtx != context) { + sliverBodyGridCtx = context; + nestedScrollUtil?.bodySliverContexts.add(context); + } + return Container( + color: Colors.green, + child: Center(child: Text('index -- $index')), + ); + }, childCount: sliverGridItemCount), + ); + } - Widget _buildNestedScrollView() { - return NestedScrollView( - key: nestedScrollViewKey, - controller: outerScrollController, - headerSliverBuilder: (context, innerBoxIsScrolled) { - return [ - SliverAppBar( - key: appBarKey, - title: const Text("NestedScrollView"), - pinned: true, - forceElevated: innerBoxIsScrolled, - ), - SliverFixedExtentList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - if (_sliverHeaderListCtx != ctx) { - _sliverHeaderListCtx = ctx; - nestedScrollUtil?.headerSliverContexts.add(ctx); - } - return ListTile( - leading: Text("Item $index"), - ); - }, - childCount: 5, - ), - itemExtent: 50, + Widget buildSliverListView() { + return SliverList( + delegate: SliverChildBuilderDelegate((ctx, index) { + if (sliverBodyListCtx != ctx) { + sliverBodyListCtx = ctx; + nestedScrollUtil?.bodySliverContexts.add(ctx); + } + return Container( + height: (index % 2 == 0) ? 80 : 50, + color: Colors.red, + child: Center( + child: Text( + "index -- $index", + style: const TextStyle(color: Colors.white), ), - SliverGrid.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - mainAxisSpacing: 10.0, - crossAxisSpacing: 10.0, - childAspectRatio: 2.0, - ), - itemBuilder: (context, index) { - if (_sliverHeaderGridCtx != context) { - _sliverHeaderGridCtx = context; - nestedScrollUtil?.headerSliverContexts.add(context); - } - return Text("Item $index"); - }, - itemCount: 10, + ), + ); + }, childCount: sliverListItemCount), + ); + } + + Widget buildNestedScrollView() { + return NestedScrollView( + key: nestedScrollViewKey, + controller: outerScrollController, + headerSliverBuilder: (context, innerBoxIsScrolled) { + return [ + SliverAppBar( + key: appBarKey, + title: const Text("NestedScrollView"), + pinned: true, + forceElevated: innerBoxIsScrolled, + ), + SliverFixedExtentList( + delegate: SliverChildBuilderDelegate((ctx, index) { + if (sliverHeaderListCtx != ctx) { + sliverHeaderListCtx = ctx; + nestedScrollUtil?.headerSliverContexts.add(ctx); + } + return ListTile(leading: Text("Item $index")); + }, childCount: 5), + itemExtent: 50, + ), + SliverGrid.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 10.0, + crossAxisSpacing: 10.0, + childAspectRatio: 2.0, ), - ]; - }, - body: Builder(builder: (context) { + itemBuilder: (context, index) { + if (sliverHeaderGridCtx != context) { + sliverHeaderGridCtx = context; + nestedScrollUtil?.headerSliverContexts.add(context); + } + return Text("Item $index"); + }, + itemCount: 10, + ), + ]; + }, + body: Builder( + builder: (context) { final innerScrollController = PrimaryScrollController.of(context); if (bodyScrollController != innerScrollController) { bodyScrollController = innerScrollController; @@ -668,768 +600,700 @@ void main() { nestedScrollUtil?.outerScrollController = outerScrollController; } return CustomScrollView( - slivers: [ - _buildSliverListView(), - _buildSliverGridView(), - ], - ); - }), - ); - } - - Widget resetAll({ - int listItemCount = 30, - int gridItemCount = 150, - }) { - sliverListItemCount = listItemCount; - sliverGridItemCount = gridItemCount; - resultMap = {}; - nestedScrollUtil = NestedScrollUtil(); - outerScrollController = ScrollController(); - bodyScrollController = null; - observerController = SliverObserverController( - controller: outerScrollController, - ); - - widget = _buildNestedScrollView(); - - widget = SliverViewObserver( - controller: observerController, - child: widget, - sliverContexts: () { - return [ - if (_sliverHeaderListCtx != null) _sliverHeaderListCtx!, - if (_sliverHeaderGridCtx != null) _sliverHeaderGridCtx!, - if (_sliverBodyListCtx != null) _sliverBodyListCtx!, - if (_sliverBodyGridCtx != null) _sliverBodyGridCtx!, - ]; - }, - customOverlap: (sliverContext) { - return nestedScrollUtil?.calcOverlap( - nestedScrollViewKey: nestedScrollViewKey, - sliverContext: sliverContext, + slivers: [buildSliverListView(), buildSliverGridView()], ); }, - onObserveAll: (result) { - resultMap = result; - }, - ); - widget = MaterialApp( - home: Material(child: widget), - ); - return widget; - } + ), + ); + } - tearDown(() { - outerScrollController.dispose(); - _sliverHeaderListCtx = null; - _sliverHeaderGridCtx = null; - _sliverBodyListCtx = null; - _sliverBodyGridCtx = null; - }); + Widget resetAll({int listItemCount = 30, int gridItemCount = 150}) { + sliverListItemCount = listItemCount; + sliverGridItemCount = gridItemCount; + resultMap = {}; + nestedScrollUtil = NestedScrollUtil(); + outerScrollController = ScrollController(); + bodyScrollController = null; + observerController = SliverObserverController( + controller: outerScrollController, + ); - testWidgets( - 'Scroll to index', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); + widget = buildNestedScrollView(); - observerController.controller = outerScrollController; - nestedScrollUtil?.jumpTo( - nestedScrollViewKey: nestedScrollViewKey, - observerController: observerController, - sliverContext: _sliverHeaderListCtx, - position: NestedScrollUtilPosition.header, - index: 1, - offset: (targetOffset) { - return calcPersistentHeaderExtent( - offset: targetOffset, - widgetKey: appBarKey, - ); - }, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - var headerListObservationResult = - (resultMap[_sliverHeaderListCtx] as ListViewObserveModel); - expect(headerListObservationResult.firstChild?.index, 1); - - observerController.controller = outerScrollController; - nestedScrollUtil?.animateTo( - nestedScrollViewKey: nestedScrollViewKey, - observerController: observerController, - sliverContext: _sliverHeaderListCtx, - position: NestedScrollUtilPosition.header, - index: 2, - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, - offset: (targetOffset) { - return calcPersistentHeaderExtent( - offset: targetOffset, - widgetKey: appBarKey, - ); - }, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - headerListObservationResult = - (resultMap[_sliverHeaderListCtx] as ListViewObserveModel); - expect(headerListObservationResult.firstChild?.index, 2); - - expect(bodyScrollController != null, true); - nestedScrollUtil?.jumpTo( + widget = SliverViewObserver( + controller: observerController, + child: widget, + sliverContexts: () { + return [ + if (sliverHeaderListCtx != null) sliverHeaderListCtx!, + if (sliverHeaderGridCtx != null) sliverHeaderGridCtx!, + if (sliverBodyListCtx != null) sliverBodyListCtx!, + if (sliverBodyGridCtx != null) sliverBodyGridCtx!, + ]; + }, + customOverlap: (sliverContext) { + return nestedScrollUtil?.calcOverlap( nestedScrollViewKey: nestedScrollViewKey, - observerController: observerController, - sliverContext: _sliverBodyListCtx, - position: NestedScrollUtilPosition.body, - index: 5, - offset: (targetOffset) { - return calcPersistentHeaderExtent( - offset: targetOffset, - widgetKey: appBarKey, - ); - }, + sliverContext: sliverContext, ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - var bodyListObservationResult = - (resultMap[_sliverBodyListCtx] as ListViewObserveModel); - expect(bodyListObservationResult.firstChild?.index, 5); + }, + onObserveAll: (result) { + resultMap = result; + }, + ); + widget = MaterialApp(home: Material(child: widget)); + return widget; + } - nestedScrollUtil?.animateTo( - nestedScrollViewKey: nestedScrollViewKey, - observerController: observerController, - sliverContext: _sliverBodyListCtx, - position: NestedScrollUtilPosition.body, - index: 20, - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, - offset: (targetOffset) { - return calcPersistentHeaderExtent( - offset: targetOffset, - widgetKey: appBarKey, - ); - }, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - bodyListObservationResult = - (resultMap[_sliverBodyListCtx] as ListViewObserveModel); - expect(bodyListObservationResult.firstChild?.index, 20); + tearDown(() { + outerScrollController.dispose(); + sliverHeaderListCtx = null; + sliverHeaderGridCtx = null; + sliverBodyListCtx = null; + sliverBodyGridCtx = null; + }); - nestedScrollUtil?.jumpTo( - nestedScrollViewKey: nestedScrollViewKey, - observerController: observerController, - sliverContext: _sliverBodyGridCtx, - position: NestedScrollUtilPosition.body, - index: 10, - offset: (targetOffset) { - return calcPersistentHeaderExtent( - offset: targetOffset, - widgetKey: appBarKey, - ); - }, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - var bodyGridObservationResult = - (resultMap[_sliverBodyGridCtx] as GridViewObserveModel); - var bodyGridFirstGroupChildIndexList = bodyGridObservationResult - .firstGroupChildList - .map((e) => e.index) - .toList(); - expect(bodyGridFirstGroupChildIndexList.contains(10), true); - - nestedScrollUtil?.animateTo( - nestedScrollViewKey: nestedScrollViewKey, - observerController: observerController, - sliverContext: _sliverBodyGridCtx, - position: NestedScrollUtilPosition.body, - index: 20, - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, - offset: (targetOffset) { - return calcPersistentHeaderExtent( - offset: targetOffset, - widgetKey: appBarKey, - ); - }, + testWidgets('Scroll to index', (tester) async { + resetAll(); + await tester.pumpWidget(widget); + + observerController.controller = outerScrollController; + nestedScrollUtil?.jumpTo( + nestedScrollViewKey: nestedScrollViewKey, + observerController: observerController, + sliverContext: sliverHeaderListCtx, + position: NestedScrollUtilPosition.header, + index: 1, + offset: (targetOffset) { + return calcPersistentHeaderExtent( + offset: targetOffset, + widgetKey: appBarKey, ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - bodyGridObservationResult = - (resultMap[_sliverBodyGridCtx] as GridViewObserveModel); - bodyGridFirstGroupChildIndexList = bodyGridObservationResult - .firstGroupChildList - .map((e) => e.index) - .toList(); - expect(bodyGridFirstGroupChildIndexList.contains(20), true); }, ); - - testWidgets( - 'Check method reset of NestedScrollUtil', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - expect(nestedScrollUtil?.headerSliverContexts.length, 2); - expect(nestedScrollUtil?.bodySliverContexts.length, 2); - expect(nestedScrollUtil?.remainingSliverContext == null, true); - expect(nestedScrollUtil?.remainingSliverRenderObj == null, true); - nestedScrollUtil?.fetchRemainingSliverContext( - nestedScrollViewKey: nestedScrollViewKey, + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + var headerListObservationResult = + (resultMap[sliverHeaderListCtx] as ListViewObserveModel); + expect(headerListObservationResult.firstChild?.index, 1); + + observerController.controller = outerScrollController; + nestedScrollUtil?.animateTo( + nestedScrollViewKey: nestedScrollViewKey, + observerController: observerController, + sliverContext: sliverHeaderListCtx, + position: NestedScrollUtilPosition.header, + index: 2, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + offset: (targetOffset) { + return calcPersistentHeaderExtent( + offset: targetOffset, + widgetKey: appBarKey, ); - expect(nestedScrollUtil?.remainingSliverContext != null, true); - expect(nestedScrollUtil?.remainingSliverRenderObj != null, true); - nestedScrollUtil?.reset(); - expect(nestedScrollUtil?.headerSliverContexts.length, 0); - expect(nestedScrollUtil?.bodySliverContexts.length, 0); - expect(nestedScrollUtil?.remainingSliverContext, null); - expect(nestedScrollUtil?.remainingSliverRenderObj, null); }, ); - - testWidgets( - 'Check the observed data when the sliver in the header is not visible', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); - await observerController.dispatchOnceObserve( - sliverContext: _sliverHeaderListCtx!, - ); - var headerListObservationResult = - (resultMap[_sliverHeaderListCtx] as ListViewObserveModel); - expect( - headerListObservationResult.displayingChildIndexList, - isNotEmpty, - ); - - nestedScrollUtil?.jumpTo( - nestedScrollViewKey: nestedScrollViewKey, - observerController: observerController, - sliverContext: _sliverHeaderGridCtx, - position: NestedScrollUtilPosition.header, - index: 0, - offset: (targetOffset) { - return calcPersistentHeaderExtent( - offset: targetOffset, - widgetKey: appBarKey, - ); - }, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - - headerListObservationResult = - (resultMap[_sliverHeaderListCtx] as ListViewObserveModel); - expect( - headerListObservationResult.displayingChildIndexList, - isEmpty, - ); - - var headerGridObservationResult = - (resultMap[_sliverHeaderGridCtx] as GridViewObserveModel); - expect( - headerGridObservationResult.firstGroupChildList.first.index, - 0, + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + headerListObservationResult = + (resultMap[sliverHeaderListCtx] as ListViewObserveModel); + expect(headerListObservationResult.firstChild?.index, 2); + + expect(bodyScrollController != null, true); + nestedScrollUtil?.jumpTo( + nestedScrollViewKey: nestedScrollViewKey, + observerController: observerController, + sliverContext: sliverBodyListCtx, + position: NestedScrollUtilPosition.body, + index: 5, + offset: (targetOffset) { + return calcPersistentHeaderExtent( + offset: targetOffset, + widgetKey: appBarKey, ); - - nestedScrollUtil?.jumpTo( - nestedScrollViewKey: nestedScrollViewKey, - observerController: observerController, - sliverContext: _sliverBodyListCtx, - position: NestedScrollUtilPosition.body, - index: 0, - offset: (targetOffset) { - return calcPersistentHeaderExtent( - offset: targetOffset, - widgetKey: appBarKey, - ); - }, + }, + ); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + var bodyListObservationResult = + (resultMap[sliverBodyListCtx] as ListViewObserveModel); + expect(bodyListObservationResult.firstChild?.index, 5); + + nestedScrollUtil?.animateTo( + nestedScrollViewKey: nestedScrollViewKey, + observerController: observerController, + sliverContext: sliverBodyListCtx, + position: NestedScrollUtilPosition.body, + index: 20, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + offset: (targetOffset) { + return calcPersistentHeaderExtent( + offset: targetOffset, + widgetKey: appBarKey, ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - - var bodyListObservationResult = - (resultMap[_sliverBodyListCtx] as ListViewObserveModel); - expect( - bodyListObservationResult.firstChild?.index, - 0, + }, + ); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + bodyListObservationResult = + (resultMap[sliverBodyListCtx] as ListViewObserveModel); + expect(bodyListObservationResult.firstChild?.index, 20); + + nestedScrollUtil?.jumpTo( + nestedScrollViewKey: nestedScrollViewKey, + observerController: observerController, + sliverContext: sliverBodyGridCtx, + position: NestedScrollUtilPosition.body, + index: 10, + offset: (targetOffset) { + return calcPersistentHeaderExtent( + offset: targetOffset, + widgetKey: appBarKey, ); - - headerGridObservationResult = - (resultMap[_sliverHeaderGridCtx] as GridViewObserveModel); - expect( - headerGridObservationResult.displayingChildIndexList, - isEmpty, + }, + ); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + var bodyGridObservationResult = + (resultMap[sliverBodyGridCtx] as GridViewObserveModel); + var bodyGridFirstGroupChildIndexList = bodyGridObservationResult + .firstGroupChildList + .map((e) => e.index) + .toList(); + expect(bodyGridFirstGroupChildIndexList.contains(10), true); + + nestedScrollUtil?.animateTo( + nestedScrollViewKey: nestedScrollViewKey, + observerController: observerController, + sliverContext: sliverBodyGridCtx, + position: NestedScrollUtilPosition.body, + index: 20, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + offset: (targetOffset) { + return calcPersistentHeaderExtent( + offset: targetOffset, + widgetKey: appBarKey, ); }, ); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + bodyGridObservationResult = + (resultMap[sliverBodyGridCtx] as GridViewObserveModel); + bodyGridFirstGroupChildIndexList = bodyGridObservationResult + .firstGroupChildList + .map((e) => e.index) + .toList(); + expect(bodyGridFirstGroupChildIndexList.contains(20), true); + }); + + testWidgets('Check method reset of NestedScrollUtil', (tester) async { + resetAll(); + await tester.pumpWidget(widget); + expect(nestedScrollUtil?.headerSliverContexts.length, 2); + expect(nestedScrollUtil?.bodySliverContexts.length, 2); + expect(nestedScrollUtil?.remainingSliverContext == null, true); + expect(nestedScrollUtil?.remainingSliverRenderObj == null, true); + nestedScrollUtil?.fetchRemainingSliverContext( + nestedScrollViewKey: nestedScrollViewKey, + ); + expect(nestedScrollUtil?.remainingSliverContext != null, true); + expect(nestedScrollUtil?.remainingSliverRenderObj != null, true); + nestedScrollUtil?.reset(); + expect(nestedScrollUtil?.headerSliverContexts.length, 0); + expect(nestedScrollUtil?.bodySliverContexts.length, 0); + expect(nestedScrollUtil?.remainingSliverContext, null); + expect(nestedScrollUtil?.remainingSliverRenderObj, null); + }); - testWidgets('Check animateTo Future completion', (tester) async { + testWidgets( + 'Check the observed data when the sliver in the header is not visible', + (tester) async { resetAll(); await tester.pumpWidget(widget); + await observerController.dispatchOnceObserve( + sliverContext: sliverHeaderListCtx!, + ); + var headerListObservationResult = + (resultMap[sliverHeaderListCtx] as ListViewObserveModel); + expect( + headerListObservationResult.displayingChildIndexList, + isNotEmpty, + ); - bool isFutureCompleted = false; - const Duration duration = Duration(seconds: 2); - - // Start to scroll - Future future = nestedScrollUtil!.animateTo( + nestedScrollUtil?.jumpTo( nestedScrollViewKey: nestedScrollViewKey, observerController: observerController, - sliverContext: _sliverBodyListCtx, - position: NestedScrollUtilPosition.body, - index: 5, - duration: duration, - curve: Curves.linear, + sliverContext: sliverHeaderGridCtx, + position: NestedScrollUtilPosition.header, + index: 0, + offset: (targetOffset) { + return calcPersistentHeaderExtent( + offset: targetOffset, + widgetKey: appBarKey, + ); + }, ); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); - future.whenComplete(() { - isFutureCompleted = true; - }); - - // Wait for scroll to start - await tester.pump(); - - // Verify scroll hasn't completed - expect(isFutureCompleted, false); + headerListObservationResult = + (resultMap[sliverHeaderListCtx] as ListViewObserveModel); + expect(headerListObservationResult.displayingChildIndexList, isEmpty); - // Wait for scroll to complete - const intervalDuration = Duration(milliseconds: 100); - int intervalTime = 0; - while (tester.binding.hasScheduledFrame) { - await tester.pump(intervalDuration); - intervalTime++; - } - // Make sure the scroll executes and completes correctly. - expect( - intervalTime, - greaterThanOrEqualTo( - duration.inMilliseconds ~/ intervalDuration.inMilliseconds, - ), - ); - expect(isFutureCompleted, true); + var headerGridObservationResult = + (resultMap[sliverHeaderGridCtx] as GridViewObserveModel); + expect(headerGridObservationResult.firstGroupChildList.first.index, 0); - // Scroll back to index 0 - isFutureCompleted = false; - intervalTime = 0; - future = nestedScrollUtil!.animateTo( + nestedScrollUtil?.jumpTo( nestedScrollViewKey: nestedScrollViewKey, observerController: observerController, - sliverContext: _sliverBodyListCtx, + sliverContext: sliverBodyListCtx, position: NestedScrollUtilPosition.body, index: 0, - duration: duration, - curve: Curves.easeInOut, + offset: (targetOffset) { + return calcPersistentHeaderExtent( + offset: targetOffset, + widgetKey: appBarKey, + ); + }, ); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); - future.whenComplete(() { - isFutureCompleted = true; - }); + var bodyListObservationResult = + (resultMap[sliverBodyListCtx] as ListViewObserveModel); + expect(bodyListObservationResult.firstChild?.index, 0); - // Wait for scroll to start - await tester.pump(); + headerGridObservationResult = + (resultMap[sliverHeaderGridCtx] as GridViewObserveModel); + expect(headerGridObservationResult.displayingChildIndexList, isEmpty); + }, + ); + + testWidgets('Check animateTo Future completion', (tester) async { + resetAll(); + await tester.pumpWidget(widget); - // Verify scroll hasn't completed - expect(isFutureCompleted, false); + bool isFutureCompleted = false; + const Duration duration = Duration(seconds: 2); + + // Start to scroll + Future future = nestedScrollUtil!.animateTo( + nestedScrollViewKey: nestedScrollViewKey, + observerController: observerController, + sliverContext: sliverBodyListCtx, + position: NestedScrollUtilPosition.body, + index: 5, + duration: duration, + curve: Curves.linear, + ); - // Wait for scroll to complete - while (tester.binding.hasScheduledFrame) { - await tester.pump(intervalDuration); - intervalTime++; - } - // Make sure the scroll executes and completes correctly. - expect( - intervalTime, - greaterThanOrEqualTo( - duration.inMilliseconds ~/ intervalDuration.inMilliseconds, - ), - ); - expect(isFutureCompleted, true); + future.whenComplete(() { + isFutureCompleted = true; }); - testWidgets('Check jumpTo Future completion', (tester) async { - int listItemCount = 100; - resetAll( - listItemCount: listItemCount, - ); - await tester.pumpWidget(widget); - - bool isFutureCompleted = false; + // Wait for scroll to start + await tester.pump(); - // Start to scroll - Future future = nestedScrollUtil!.jumpTo( - nestedScrollViewKey: nestedScrollViewKey, - observerController: observerController, - sliverContext: _sliverBodyListCtx, - position: NestedScrollUtilPosition.body, - // Need to use an unrendered item's index to force paging to find it. - index: listItemCount - 1, - ); + // Verify scroll hasn't completed + expect(isFutureCompleted, false); - future.whenComplete(() { - isFutureCompleted = true; - }); + // Wait for scroll to complete + const intervalDuration = Duration(milliseconds: 100); + int intervalTime = 0; + while (tester.binding.hasScheduledFrame) { + await tester.pump(intervalDuration); + intervalTime++; + } + // Make sure the scroll executes and completes correctly. + expect( + intervalTime, + greaterThanOrEqualTo( + duration.inMilliseconds ~/ intervalDuration.inMilliseconds, + ), + ); + expect(isFutureCompleted, true); + + // Scroll back to index 0 + isFutureCompleted = false; + intervalTime = 0; + future = nestedScrollUtil!.animateTo( + nestedScrollViewKey: nestedScrollViewKey, + observerController: observerController, + sliverContext: sliverBodyListCtx, + position: NestedScrollUtilPosition.body, + index: 0, + duration: duration, + curve: Curves.easeInOut, + ); - // Wait for scroll to start - await tester.pump(); + future.whenComplete(() { + isFutureCompleted = true; + }); - // Verify scroll hasn't completed - expect(isFutureCompleted, false); + // Wait for scroll to start + await tester.pump(); - // Wait for scroll to complete - const intervalDuration = Duration(milliseconds: 100); - int intervalTime = 0; - while (tester.binding.hasScheduledFrame) { - await tester.pump(intervalDuration); - intervalTime++; - } - // Make sure the scroll executes and completes correctly. - expect( - intervalTime, - greaterThanOrEqualTo(0), - ); - expect(isFutureCompleted, true); + // Verify scroll hasn't completed + expect(isFutureCompleted, false); - // Scroll back to index 0 - isFutureCompleted = false; - intervalTime = 0; - future = nestedScrollUtil!.jumpTo( - nestedScrollViewKey: nestedScrollViewKey, - observerController: observerController, - sliverContext: _sliverBodyListCtx, - position: NestedScrollUtilPosition.body, - index: 0, - ); + // Wait for scroll to complete + while (tester.binding.hasScheduledFrame) { + await tester.pump(intervalDuration); + intervalTime++; + } + // Make sure the scroll executes and completes correctly. + expect( + intervalTime, + greaterThanOrEqualTo( + duration.inMilliseconds ~/ intervalDuration.inMilliseconds, + ), + ); + expect(isFutureCompleted, true); + }); - future.whenComplete(() { - isFutureCompleted = true; - }); + testWidgets('Check jumpTo Future completion', (tester) async { + int listItemCount = 100; + resetAll(listItemCount: listItemCount); + await tester.pumpWidget(widget); - // Wait for scroll to start - await tester.pump(); + bool isFutureCompleted = false; - // Verify scroll hasn't completed - expect(isFutureCompleted, false); + // Start to scroll + Future future = nestedScrollUtil!.jumpTo( + nestedScrollViewKey: nestedScrollViewKey, + observerController: observerController, + sliverContext: sliverBodyListCtx, + position: NestedScrollUtilPosition.body, + // Need to use an unrendered item's index to force paging to find it. + index: listItemCount - 1, + ); - // Wait for scroll to complete - while (tester.binding.hasScheduledFrame) { - await tester.pump(intervalDuration); - intervalTime++; - } - // Make sure the scroll executes and completes correctly. - expect( - intervalTime, - greaterThanOrEqualTo(0), - ); - expect(isFutureCompleted, true); + future.whenComplete(() { + isFutureCompleted = true; }); - }, - ); - - group( - 'Configure center in CustomScrollView', - () { - late Widget widget; - BuildContext? _sliverListCtx1; - BuildContext? _sliverListCtx2; - BuildContext? _sliverListCtx3; - BuildContext? _sliverListCtx4; - final _centerKey = GlobalKey(); - ScrollController scrollController = ScrollController(); - late SliverObserverController observerController; - Map resultMap = {}; - - Widget _buildSliverListView({ - required Color color, - Function(BuildContext)? onBuild, - }) { - return SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, index) { - onBuild?.call(ctx); - final int itemIndex = index ~/ 2; - return Container( - height: (itemIndex % 2 == 0) ? 80 : 50, - color: color, - child: Center( - child: Text( - "index -- $index", - ), - ), - ); - }, - childCount: 100, - ), - ); - } - - Widget _buildScrollView({ - required double anchor, - }) { - return CustomScrollView( - center: _centerKey, - anchor: anchor, - controller: scrollController, - slivers: [ - _buildSliverListView( - color: Colors.redAccent, - onBuild: (ctx) { - _sliverListCtx1 = ctx; - }, - ), - _buildSliverListView( - color: Colors.blueGrey, - onBuild: (ctx) { - _sliverListCtx2 = ctx; - }, - ), - SliverPadding(padding: EdgeInsets.zero, key: _centerKey), - _buildSliverListView( - color: Colors.teal, - onBuild: (ctx) { - _sliverListCtx3 = ctx; - }, - ), - _buildSliverListView( - color: Colors.purple, - onBuild: (ctx) { - _sliverListCtx4 = ctx; - }, - ), - ], - ); - } - Widget resetAll({ - required double anchor, - }) { - resultMap = {}; - scrollController = ScrollController(); - observerController = SliverObserverController( - controller: scrollController, - ); + // Wait for scroll to start + await tester.pump(); - widget = _buildScrollView(anchor: anchor); + // Verify scroll hasn't completed + expect(isFutureCompleted, false); - widget = SliverViewObserver( - controller: observerController, - child: widget, - sliverContexts: () { - return [ - if (_sliverListCtx1 != null) _sliverListCtx1!, - if (_sliverListCtx2 != null) _sliverListCtx2!, - if (_sliverListCtx3 != null) _sliverListCtx3!, - if (_sliverListCtx4 != null) _sliverListCtx4!, - ]; - }, - onObserveAll: (result) { - resultMap = result; - }, - ); - widget = MaterialApp( - home: Material(child: widget), - ); - return widget; + // Wait for scroll to complete + const intervalDuration = Duration(milliseconds: 100); + int intervalTime = 0; + while (tester.binding.hasScheduledFrame) { + await tester.pump(intervalDuration); + intervalTime++; } + // Make sure the scroll executes and completes correctly. + expect(intervalTime, greaterThanOrEqualTo(0)); + expect(isFutureCompleted, true); + + // Scroll back to index 0 + isFutureCompleted = false; + intervalTime = 0; + future = nestedScrollUtil!.jumpTo( + nestedScrollViewKey: nestedScrollViewKey, + observerController: observerController, + sliverContext: sliverBodyListCtx, + position: NestedScrollUtilPosition.body, + index: 0, + ); - tearDown(() { - scrollController.dispose(); - _sliverListCtx1 = null; - _sliverListCtx2 = null; - _sliverListCtx3 = null; - _sliverListCtx4 = null; + future.whenComplete(() { + isFutureCompleted = true; }); - testWidgets('Check isForwardGrowthDirection', (tester) async { - resetAll(anchor: 0.5); - await tester.pumpWidget(widget); - - final _sliverListObj1 = ObserverUtils.findRenderObject(_sliverListCtx1); - expect(_sliverListObj1 is RenderSliverMultiBoxAdaptor, true); - _sliverListObj1 as RenderSliverMultiBoxAdaptor; - expect(_sliverListObj1.isForwardGrowthDirection, false); - - final _sliverListObj2 = ObserverUtils.findRenderObject(_sliverListCtx2); - expect(_sliverListObj2 is RenderSliverMultiBoxAdaptor, true); - _sliverListObj2 as RenderSliverMultiBoxAdaptor; - expect(_sliverListObj2.isForwardGrowthDirection, false); - - final _sliverListObj3 = ObserverUtils.findRenderObject(_sliverListCtx3); - expect(_sliverListObj3 is RenderSliverMultiBoxAdaptor, true); - _sliverListObj3 as RenderSliverMultiBoxAdaptor; - expect(_sliverListObj3.isForwardGrowthDirection, true); - - final _sliverListObj4 = ObserverUtils.findRenderObject(_sliverListCtx4); - expect(_sliverListObj4 is RenderSliverMultiBoxAdaptor, true); - _sliverListObj4 as RenderSliverMultiBoxAdaptor; - expect(_sliverListObj4.isForwardGrowthDirection, true); - }); + // Wait for scroll to start + await tester.pump(); - testWidgets('Check viewportExtremeScrollExtent and rectify', - (tester) async { - resetAll(anchor: 0.5); - await tester.pumpWidget(widget); + // Verify scroll hasn't completed + expect(isFutureCompleted, false); - final _sliverListObj1 = ObserverUtils.findRenderObject(_sliverListCtx1); - expect(_sliverListObj1 != null, true); - expect(_sliverListObj1 is RenderSliverMultiBoxAdaptor, true); - _sliverListObj1 as RenderSliverMultiBoxAdaptor; - var extremeScrollExtent = - observerController.viewportExtremeScrollExtent( - viewport: ObserverUtils.findViewport(_sliverListObj1)!, - obj: _sliverListObj1, - ); - expect(extremeScrollExtent <= 0, true); - expect(extremeScrollExtent.rectify(_sliverListObj1) >= 0, true); - - final _sliverListObj2 = ObserverUtils.findRenderObject(_sliverListCtx2); - expect(_sliverListObj2 != null, true); - expect(_sliverListObj2 is RenderSliverMultiBoxAdaptor, true); - _sliverListObj2 as RenderSliverMultiBoxAdaptor; - extremeScrollExtent = observerController.viewportExtremeScrollExtent( - viewport: ObserverUtils.findViewport(_sliverListObj2)!, - obj: _sliverListObj2, - ); - expect(extremeScrollExtent <= 0, true); - expect(extremeScrollExtent.rectify(_sliverListObj2) >= 0, true); - - final _sliverListObj3 = ObserverUtils.findRenderObject(_sliverListCtx3); - expect(_sliverListObj3 != null, true); - expect(_sliverListObj3 is RenderSliverMultiBoxAdaptor, true); - _sliverListObj3 as RenderSliverMultiBoxAdaptor; - extremeScrollExtent = observerController.viewportExtremeScrollExtent( - viewport: ObserverUtils.findViewport(_sliverListObj3)!, - obj: _sliverListObj3, - ); - expect(extremeScrollExtent >= 0, true); - expect(extremeScrollExtent.rectify(_sliverListObj3) >= 0, true); - - final _sliverListObj4 = ObserverUtils.findRenderObject(_sliverListCtx4); - expect(_sliverListObj4 != null, true); - expect(_sliverListObj4 is RenderSliverMultiBoxAdaptor, true); - _sliverListObj4 as RenderSliverMultiBoxAdaptor; - extremeScrollExtent = observerController.viewportExtremeScrollExtent( - viewport: ObserverUtils.findViewport(_sliverListObj4)!, - obj: _sliverListObj4, - ); - expect(extremeScrollExtent >= 0, true); - expect(extremeScrollExtent.rectify(_sliverListObj4) >= 0, true); - }); + // Wait for scroll to complete + while (tester.binding.hasScheduledFrame) { + await tester.pump(intervalDuration); + intervalTime++; + } + // Make sure the scroll executes and completes correctly. + expect(intervalTime, greaterThanOrEqualTo(0)); + expect(isFutureCompleted, true); + }); + }); - testWidgets( - 'Scroll to index with anchor 1.0', - (tester) async { - resetAll(anchor: 1.0); - await tester.pumpWidget(widget); + group('Configure center in CustomScrollView', () { + late Widget widget; + BuildContext? sliverListCtx1; + BuildContext? sliverListCtx2; + BuildContext? sliverListCtx3; + BuildContext? sliverListCtx4; + final centerKey = GlobalKey(); + ScrollController scrollController = ScrollController(); + late SliverObserverController observerController; + Map resultMap = {}; - observerController.jumpTo( - index: 1, - sliverContext: _sliverListCtx1, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - final sliverList1ObservationResult = - (resultMap[_sliverListCtx1] as ListViewObserveModel); - expect(sliverList1ObservationResult.firstChild?.index, 1); - - observerController.jumpTo( - index: 5, - sliverContext: _sliverListCtx2, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - final sliverList2ObservationResult = - (resultMap[_sliverListCtx2] as ListViewObserveModel); - expect(sliverList2ObservationResult.firstChild?.index, 5); - - observerController.jumpTo( - index: 10, - sliverContext: _sliverListCtx3, - alignment: 1, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - final sliverList3ObservationResult = - (resultMap[_sliverListCtx3] as ListViewObserveModel); - expect( - sliverList3ObservationResult.displayingChildModelList.last.index, - 10, + Widget buildSliverListView({ + required Color color, + Function(BuildContext)? onBuild, + }) { + return SliverList( + delegate: SliverChildBuilderDelegate((ctx, index) { + onBuild?.call(ctx); + final int itemIndex = index ~/ 2; + return Container( + height: (itemIndex % 2 == 0) ? 80 : 50, + color: color, + child: Center(child: Text("index -- $index")), ); + }, childCount: 100), + ); + } - observerController.jumpTo( - index: 8, - sliverContext: _sliverListCtx4, - alignment: 1, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - final sliverList4ObservationResult = - (resultMap[_sliverListCtx4] as ListViewObserveModel); - expect( - sliverList4ObservationResult.displayingChildModelList.last.index, - 8, - ); + Widget buildScrollView({required double anchor}) { + return CustomScrollView( + center: centerKey, + anchor: anchor, + controller: scrollController, + slivers: [ + buildSliverListView( + color: Colors.redAccent, + onBuild: (ctx) { + sliverListCtx1 = ctx; + }, + ), + buildSliverListView( + color: Colors.blueGrey, + onBuild: (ctx) { + sliverListCtx2 = ctx; + }, + ), + SliverPadding(padding: EdgeInsets.zero, key: centerKey), + buildSliverListView( + color: Colors.teal, + onBuild: (ctx) { + sliverListCtx3 = ctx; + }, + ), + buildSliverListView( + color: Colors.purple, + onBuild: (ctx) { + sliverListCtx4 = ctx; + }, + ), + ], + ); + } + + Widget resetAll({required double anchor}) { + resultMap = {}; + scrollController = ScrollController(); + observerController = SliverObserverController( + controller: scrollController, + ); + + widget = buildScrollView(anchor: anchor); + + widget = SliverViewObserver( + controller: observerController, + child: widget, + sliverContexts: () { + return [ + if (sliverListCtx1 != null) sliverListCtx1!, + if (sliverListCtx2 != null) sliverListCtx2!, + if (sliverListCtx3 != null) sliverListCtx3!, + if (sliverListCtx4 != null) sliverListCtx4!, + ]; + }, + onObserveAll: (result) { + resultMap = result; }, ); + widget = MaterialApp(home: Material(child: widget)); + return widget; + } - testWidgets( - 'Scroll to index with anchor 0.0', - (tester) async { - resetAll(anchor: 0.0); - await tester.pumpWidget(widget); + tearDown(() { + scrollController.dispose(); + sliverListCtx1 = null; + sliverListCtx2 = null; + sliverListCtx3 = null; + sliverListCtx4 = null; + }); - observerController.jumpTo( - index: 1, - sliverContext: _sliverListCtx1, - alignment: 1, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - final sliverList1ObservationResult = - (resultMap[_sliverListCtx1] as ListViewObserveModel); - expect( - sliverList1ObservationResult.displayingChildModelList.last.index, - 1, - ); + testWidgets('Check isForwardGrowthDirection', (tester) async { + resetAll(anchor: 0.5); + await tester.pumpWidget(widget); - observerController.jumpTo( - index: 5, - sliverContext: _sliverListCtx2, - alignment: 1, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - final sliverList2ObservationResult = - (resultMap[_sliverListCtx2] as ListViewObserveModel); - expect( - sliverList2ObservationResult.displayingChildModelList.last.index, - 5, - ); + final sliverListObj1 = ObserverUtils.findRenderObject(sliverListCtx1); + expect(sliverListObj1 is RenderSliverMultiBoxAdaptor, true); + sliverListObj1 as RenderSliverMultiBoxAdaptor; + expect(sliverListObj1.isForwardGrowthDirection, false); + + final sliverListObj2 = ObserverUtils.findRenderObject(sliverListCtx2); + expect(sliverListObj2 is RenderSliverMultiBoxAdaptor, true); + sliverListObj2 as RenderSliverMultiBoxAdaptor; + expect(sliverListObj2.isForwardGrowthDirection, false); + + final sliverListObj3 = ObserverUtils.findRenderObject(sliverListCtx3); + expect(sliverListObj3 is RenderSliverMultiBoxAdaptor, true); + sliverListObj3 as RenderSliverMultiBoxAdaptor; + expect(sliverListObj3.isForwardGrowthDirection, true); + + final sliverListObj4 = ObserverUtils.findRenderObject(sliverListCtx4); + expect(sliverListObj4 is RenderSliverMultiBoxAdaptor, true); + sliverListObj4 as RenderSliverMultiBoxAdaptor; + expect(sliverListObj4.isForwardGrowthDirection, true); + }); - observerController.jumpTo( - index: 10, - sliverContext: _sliverListCtx3, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - final sliverList3ObservationResult = - (resultMap[_sliverListCtx3] as ListViewObserveModel); - expect(sliverList3ObservationResult.firstChild?.index, 10); - - observerController.jumpTo( - index: 8, - sliverContext: _sliverListCtx4, - ); - await tester.pumpAndSettle(); - await tester.pump(observerController.observeIntervalForScrolling); - final sliverList4ObservationResult = - (resultMap[_sliverListCtx4] as ListViewObserveModel); - expect(sliverList4ObservationResult.firstChild?.index, 8); - }, + testWidgets('Check viewportExtremeScrollExtent and rectify', ( + tester, + ) async { + resetAll(anchor: 0.5); + await tester.pumpWidget(widget); + + final sliverListObj1 = ObserverUtils.findRenderObject(sliverListCtx1); + expect(sliverListObj1 != null, true); + expect(sliverListObj1 is RenderSliverMultiBoxAdaptor, true); + sliverListObj1 as RenderSliverMultiBoxAdaptor; + var extremeScrollExtent = observerController.viewportExtremeScrollExtent( + viewport: ObserverUtils.findViewport(sliverListObj1)!, + obj: sliverListObj1, + ); + expect(extremeScrollExtent <= 0, true); + expect(extremeScrollExtent.rectify(sliverListObj1) >= 0, true); + + final sliverListObj2 = ObserverUtils.findRenderObject(sliverListCtx2); + expect(sliverListObj2 != null, true); + expect(sliverListObj2 is RenderSliverMultiBoxAdaptor, true); + sliverListObj2 as RenderSliverMultiBoxAdaptor; + extremeScrollExtent = observerController.viewportExtremeScrollExtent( + viewport: ObserverUtils.findViewport(sliverListObj2)!, + obj: sliverListObj2, + ); + expect(extremeScrollExtent <= 0, true); + expect(extremeScrollExtent.rectify(sliverListObj2) >= 0, true); + + final sliverListObj3 = ObserverUtils.findRenderObject(sliverListCtx3); + expect(sliverListObj3 != null, true); + expect(sliverListObj3 is RenderSliverMultiBoxAdaptor, true); + sliverListObj3 as RenderSliverMultiBoxAdaptor; + extremeScrollExtent = observerController.viewportExtremeScrollExtent( + viewport: ObserverUtils.findViewport(sliverListObj3)!, + obj: sliverListObj3, + ); + expect(extremeScrollExtent >= 0, true); + expect(extremeScrollExtent.rectify(sliverListObj3) >= 0, true); + + final sliverListObj4 = ObserverUtils.findRenderObject(sliverListCtx4); + expect(sliverListObj4 != null, true); + expect(sliverListObj4 is RenderSliverMultiBoxAdaptor, true); + sliverListObj4 as RenderSliverMultiBoxAdaptor; + extremeScrollExtent = observerController.viewportExtremeScrollExtent( + viewport: ObserverUtils.findViewport(sliverListObj4)!, + obj: sliverListObj4, + ); + expect(extremeScrollExtent >= 0, true); + expect(extremeScrollExtent.rectify(sliverListObj4) >= 0, true); + }); + + testWidgets('Scroll to index with anchor 1.0', (tester) async { + resetAll(anchor: 1.0); + await tester.pumpWidget(widget); + + observerController.jumpTo(index: 1, sliverContext: sliverListCtx1); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + final sliverList1ObservationResult = + (resultMap[sliverListCtx1] as ListViewObserveModel); + expect(sliverList1ObservationResult.firstChild?.index, 1); + + observerController.jumpTo(index: 5, sliverContext: sliverListCtx2); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + final sliverList2ObservationResult = + (resultMap[sliverListCtx2] as ListViewObserveModel); + expect(sliverList2ObservationResult.firstChild?.index, 5); + + observerController.jumpTo( + index: 10, + sliverContext: sliverListCtx3, + alignment: 1, + ); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + final sliverList3ObservationResult = + (resultMap[sliverListCtx3] as ListViewObserveModel); + expect( + sliverList3ObservationResult.displayingChildModelList.last.index, + 10, + ); + + observerController.jumpTo( + index: 8, + sliverContext: sliverListCtx4, + alignment: 1, + ); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + final sliverList4ObservationResult = + (resultMap[sliverListCtx4] as ListViewObserveModel); + expect( + sliverList4ObservationResult.displayingChildModelList.last.index, + 8, + ); + }); + + testWidgets('Scroll to index with anchor 0.0', (tester) async { + resetAll(anchor: 0.0); + await tester.pumpWidget(widget); + + observerController.jumpTo( + index: 1, + sliverContext: sliverListCtx1, + alignment: 1, + ); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + final sliverList1ObservationResult = + (resultMap[sliverListCtx1] as ListViewObserveModel); + expect( + sliverList1ObservationResult.displayingChildModelList.last.index, + 1, + ); + + observerController.jumpTo( + index: 5, + sliverContext: sliverListCtx2, + alignment: 1, + ); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + final sliverList2ObservationResult = + (resultMap[sliverListCtx2] as ListViewObserveModel); + expect( + sliverList2ObservationResult.displayingChildModelList.last.index, + 5, ); - }, - ); + + observerController.jumpTo(index: 10, sliverContext: sliverListCtx3); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + final sliverList3ObservationResult = + (resultMap[sliverListCtx3] as ListViewObserveModel); + expect(sliverList3ObservationResult.firstChild?.index, 10); + + observerController.jumpTo(index: 8, sliverContext: sliverListCtx4); + await tester.pumpAndSettle(); + await tester.pump(observerController.observeIntervalForScrolling); + final sliverList4ObservationResult = + (resultMap[sliverListCtx4] as ListViewObserveModel); + expect(sliverList4ObservationResult.firstChild?.index, 8); + }); + }); group('ObserverListener', () { late String tag1; @@ -1445,9 +1309,7 @@ void main() { scrollController.dispose(); }); - resetAll({ - bool isResetTag = false, - }) { + resetAll({bool isResetTag = false}) { if (isResetTag) { tag1 = tag1 * 2; tag2 = tag2 * 2; @@ -1464,24 +1326,20 @@ void main() { controller: scrollController, ); } - widget = _buildScrollView( + widget = buildScrollView( scrollController: scrollController, listItemBuilder: (context, index) { if (index == 3) { return const SizedBox( height: 50, - child: Center( - child: Icon(Icons.list), - ), + child: Center(child: Icon(Icons.list)), ); } return Container(height: 50); }, gridItemBuilder: (context, index) { if (index == 3) { - return const Center( - child: Icon(Icons.grid_view), - ); + return const Center(child: Icon(Icons.grid_view)); } return Container(); }, @@ -1490,21 +1348,21 @@ void main() { key: key2, tag: tag2, sliverContexts: () => [ - if (_sliverListCtx != null) _sliverListCtx!, - if (_sliverGridCtx != null) _sliverGridCtx! + if (sliverListCtx != null) sliverListCtx!, + if (sliverGridCtx != null) sliverGridCtx!, ], - child: widget, controller: observerController2, + child: widget, ); widget = SliverViewObserver( key: key1, tag: tag1, sliverContexts: () => [ - if (_sliverListCtx != null) _sliverListCtx!, - if (_sliverGridCtx != null) _sliverGridCtx! + if (sliverListCtx != null) sliverListCtx!, + if (sliverGridCtx != null) sliverGridCtx!, ], - child: widget, controller: observerController1, + child: widget, ); } @@ -1549,9 +1407,7 @@ void main() { cbResult = result; } - onObserveAllCallback( - Map resultMap, - ) { + onObserveAllCallback(Map resultMap) { cbAllResult = resultMap; } @@ -1587,9 +1443,9 @@ void main() { ScrollViewOnceObserveNotificationResult? result = await observerController2.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - isDependObserveCallback: false, - ); + sliverContext: sliverListCtx!, + isDependObserveCallback: false, + ); expect(result.observeResult, cbResult); expect(result.observeAllResult, cbAllResult); expect(result.observeViewportResultModel, cbObserveViewportResult); @@ -1601,10 +1457,7 @@ void main() { ); expect(listObserverState.innerListeners?.length, 0); - observerController2.jumpTo( - index: 0, - sliverContext: _sliverGridCtx, - ); + observerController2.jumpTo(index: 0, sliverContext: sliverGridCtx); await tester.pumpAndSettle(); await tester.pump(observerController2.observeIntervalForScrolling); cbResult = null; @@ -1639,7 +1492,7 @@ void main() { expect(gridObserverState.innerListeners?.length, 1); result = await observerController2.dispatchOnceObserve( - sliverContext: _sliverListCtx!, + sliverContext: sliverListCtx!, ); expect(result.observeResult, cbResult); expect(result.observeAllResult, cbAllResult); @@ -1672,9 +1525,7 @@ void main() { cbResult = result; } - onObserveAllCallback( - Map resultMap, - ) { + onObserveAllCallback(Map resultMap) { cbAllResult = resultMap; } @@ -1711,9 +1562,9 @@ void main() { ScrollViewOnceObserveNotificationResult? result = await observerController2.dispatchOnceObserve( - sliverContext: _sliverListCtx!, - isDependObserveCallback: false, - ); + sliverContext: sliverListCtx!, + isDependObserveCallback: false, + ); expect(result.observeResult, cbResult); expect(result.observeAllResult, cbAllResult); expect(result.observeViewportResultModel, cbObserveViewportResult); @@ -1726,10 +1577,7 @@ void main() { expect(listObserverState?.innerSliverListeners?.length, 0); expect(listObserverState?.innerListeners?.length, 0); - observerController2.jumpTo( - index: 0, - sliverContext: _sliverGridCtx, - ); + observerController2.jumpTo(index: 0, sliverContext: sliverGridCtx); await tester.pumpAndSettle(); await tester.pump(observerController2.observeIntervalForScrolling); cbResult = null; @@ -1764,7 +1612,7 @@ void main() { expect(gridObserverState?.innerListeners?.length, 1); result = await observerController2.dispatchOnceObserve( - sliverContext: _sliverListCtx!, + sliverContext: sliverListCtx!, isDependObserveCallback: false, ); expect(result.observeResult, cbResult); @@ -1782,67 +1630,61 @@ void main() { // Regression test for https://github.com/fluttercandies/flutter_scrollview_observer/issues/120 testWidgets( - 'No exception when MixViewObserverState is disposed during scrolling', - (tester) async { - resetAll(); - await tester.pumpWidget(widget); + 'No exception when MixViewObserverState is disposed during scrolling', + (tester) async { + resetAll(); + await tester.pumpWidget(widget); - observerController1.animateTo( - index: 60, - sliverContext: _sliverListCtx, - duration: const Duration(seconds: 3), - curve: Curves.easeInOut, - ); - await tester.pump(); - await tester.pump(const Duration(seconds: 1)); - await tester.pumpWidget(Container()); - }); + observerController1.animateTo( + index: 60, + sliverContext: sliverListCtx, + duration: const Duration(seconds: 3), + curve: Curves.easeInOut, + ); + await tester.pump(); + await tester.pump(const Duration(seconds: 1)); + await tester.pumpWidget(Container()); + }, + ); testWidgets( - 'innerTagChangeCount should not increase when tag remains unchanged', - (tester) async { - const String tag = 'tag1'; - scrollController = ScrollController(); - Widget scrollView = _buildScrollView(scrollController: scrollController); + 'innerTagChangeCount should not increase when tag remains unchanged', + (tester) async { + const String tag = 'tag1'; + scrollController = ScrollController(); + Widget scrollView = buildScrollView(scrollController: scrollController); - widget = SliverViewObserver( - tag: tag, - child: scrollView, - ); + widget = SliverViewObserver(tag: tag, child: scrollView); - await tester.pumpWidget(widget); - await tester.pumpAndSettle(); + await tester.pumpWidget(widget); + await tester.pumpAndSettle(); - // Get ObserverWidgetState - final itemFinder = find.byType(SliverViewObserver); - final observerState = tester.state(itemFinder); - expect(observerState, isNotNull); + // Get ObserverWidgetState + final itemFinder = find.byType(SliverViewObserver); + final observerState = tester.state(itemFinder); + expect(observerState, isNotNull); - // Record initial tagChangeCount - final initialTagChangeCount = observerState.innerTagChangeCount; + // Record initial tagChangeCount + final initialTagChangeCount = observerState.innerTagChangeCount; - // Refresh widget but keep tag unchanged - widget = SliverViewObserver( - tag: tag, - child: scrollView, - ); - await tester.pumpWidget(widget); - await tester.pumpAndSettle(); + // Refresh widget but keep tag unchanged + widget = SliverViewObserver(tag: tag, child: scrollView); + await tester.pumpWidget(widget); + await tester.pumpAndSettle(); - // Verify that tagChangeCount has not increased - expect(observerState.innerTagChangeCount, initialTagChangeCount); - expect(observerState.innerTagChangeCount, 0); - }); + // Verify that tagChangeCount has not increased + expect(observerState.innerTagChangeCount, initialTagChangeCount); + expect(observerState.innerTagChangeCount, 0); + }, + ); - testWidgets('No exception in _checkTagChange during refresh and dispose', - (tester) async { + testWidgets('No exception in _checkTagChange during refresh and dispose', ( + tester, + ) async { // Regression test for https://github.com/fluttercandies/flutter_scrollview_observer/issues/143 scrollController = ScrollController(); - Widget scrollView = _buildScrollView(scrollController: scrollController); - widget = SliverViewObserver( - tag: 'tag1', - child: scrollView, - ); + Widget scrollView = buildScrollView(scrollController: scrollController); + widget = SliverViewObserver(tag: 'tag1', child: scrollView); await tester.pumpWidget(widget); // Get ObserverWidgetState @@ -1853,10 +1695,7 @@ void main() { final completer = Completer(); observerState.innerCheckTagChangeEndOfFrame = completer.future; - widget = SliverViewObserver( - tag: 'tag2', - child: scrollView, - ); + widget = SliverViewObserver(tag: 'tag2', child: scrollView); await tester.pumpWidget(widget); // Dispose widget before endOfFrame completes @@ -1872,8 +1711,9 @@ void main() { }); group('cancelOnceObserveNotificationBubbling', () { - testWidgets('cancelOnceObserveNotificationBubbling is true (default)', - (tester) async { + testWidgets('cancelOnceObserveNotificationBubbling is true (default)', ( + tester, + ) async { final scrollController = ScrollController(); final observerController = SliverObserverController( controller: scrollController, @@ -1915,8 +1755,9 @@ void main() { scrollController.dispose(); }); - testWidgets('cancelOnceObserveNotificationBubbling is false', - (tester) async { + testWidgets('cancelOnceObserveNotificationBubbling is false', ( + tester, + ) async { final scrollController = ScrollController(); final observerController = SliverObserverController( controller: scrollController,