From 217ecd439c5ea97a759108efec37108fb3b3bf2e Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Sun, 25 Jun 2023 17:45:08 +0900 Subject: [PATCH 01/11] Add state type --- lib/src/widgets/sliver_sticky_header.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index 48d0657..c2f8d42 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -134,6 +134,13 @@ class SliverStickyHeaderState { } } +enum SliverStickyHeaderLocation { + pushed, + notPinned, + pinned, + settling, +} + /// A sliver that displays a header before its sliver. /// The header scrolls off the viewport only when the sliver does. /// From 4e9b56e80a1d490cde930f782587fa6cb49786b8 Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Sun, 25 Jun 2023 18:26:07 +0900 Subject: [PATCH 02/11] Renames --- lib/src/widgets/sliver_sticky_header.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index c2f8d42..0520b6a 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -134,9 +134,9 @@ class SliverStickyHeaderState { } } -enum SliverStickyHeaderLocation { +enum SliverStickyHeaderActivity { pushed, - notPinned, + unpinned, pinned, settling, } From 07f5a7c6cfc909d032640c9e3c7237e867709715 Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Sun, 25 Jun 2023 18:38:50 +0900 Subject: [PATCH 03/11] Add the activity handler --- lib/src/rendering/sliver_sticky_header.dart | 34 +++++++++++++++++++++ lib/src/widgets/sliver_sticky_header.dart | 12 +++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 9a4503f..0faf644 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -2,6 +2,7 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:value_layout_builder/value_layout_builder.dart'; @@ -16,6 +17,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { bool overlapsContent: false, bool sticky: true, StickyHeaderController? controller, + this.activityHandler, }) : _overlapsContent = overlapsContent, _sticky = sticky, _controller = controller { @@ -23,6 +25,9 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { this.child = child; } + SliverStickyHeaderActivityHandler? activityHandler; + SliverStickyHeaderActivity? _lastReportedActivity; + SliverStickyHeaderState? _oldState; double? _headerExtent; late bool _isPinned; @@ -261,6 +266,9 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { controller?.stickyHeaderScrollOffset = constraints.precedingScrollExtent; } + + _tryNotifyActivity(headerScrollRatio); + // second layout if scroll percentage changed and header is a // RenderStickyHeaderLayoutBuilder. if (header is RenderConstrainedLayoutBuilder< @@ -300,6 +308,32 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { } } + void _tryNotifyActivity(double headerScrollRatio) { + final SliverStickyHeaderActivity location; + if (_isPinned) { + if (headerScrollRatio >= 1) { + location = SliverStickyHeaderActivity.pushed; + } else if (headerScrollRatio > 0) { + location = SliverStickyHeaderActivity.settling; + } else { + location = SliverStickyHeaderActivity.pinned; + } + } else { + location = SliverStickyHeaderActivity.unpinned; + } + + if (activityHandler != null && + _lastReportedActivity != null && + location != _lastReportedActivity) { + WidgetsBinding.instance.scheduleTask( + () => activityHandler?.call(location), + Priority.touch, + ); + } + + _lastReportedActivity = location; + } + @override bool hitTestChildren(SliverHitTestResult result, {required double mainAxisPosition, required double crossAxisPosition}) { diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index 0520b6a..b34d068 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -134,6 +134,9 @@ class SliverStickyHeaderState { } } +typedef SliverStickyHeaderActivityHandler = void Function( + SliverStickyHeaderActivity activity); + enum SliverStickyHeaderActivity { pushed, unpinned, @@ -162,6 +165,7 @@ class SliverStickyHeader extends RenderObjectWidget { this.overlapsContent: false, this.sticky = true, this.controller, + this.activityHandler, }) : super(key: key); /// Creates a widget that builds the header of a [SliverStickyHeader] @@ -178,6 +182,7 @@ class SliverStickyHeader extends RenderObjectWidget { bool overlapsContent: false, bool sticky = true, StickyHeaderController? controller, + SliverStickyHeaderActivityHandler? activityHandler, }) : this( key: key, header: ValueLayoutBuilder( @@ -188,6 +193,7 @@ class SliverStickyHeader extends RenderObjectWidget { overlapsContent: overlapsContent, sticky: sticky, controller: controller, + activityHandler: activityHandler, ); /// The header to display before the sliver. @@ -210,12 +216,15 @@ class SliverStickyHeader extends RenderObjectWidget { /// will be used. final StickyHeaderController? controller; + final SliverStickyHeaderActivityHandler? activityHandler; + @override RenderSliverStickyHeader createRenderObject(BuildContext context) { return RenderSliverStickyHeader( overlapsContent: overlapsContent, sticky: sticky, controller: controller ?? DefaultStickyHeaderController.of(context), + activityHandler: activityHandler, ); } @@ -231,7 +240,8 @@ class SliverStickyHeader extends RenderObjectWidget { renderObject ..overlapsContent = overlapsContent ..sticky = sticky - ..controller = controller ?? DefaultStickyHeaderController.of(context); + ..controller = controller ?? DefaultStickyHeaderController.of(context) + ..activityHandler = activityHandler; } } From 191d31ce136826bdc3ab3292d1e460596ca9a4ae Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Sun, 25 Jun 2023 19:13:24 +0900 Subject: [PATCH 04/11] Add example --- example/lib/examples/activity_handler.dart | 72 ++++++++++++++++++++++ example/lib/main.dart | 5 ++ 2 files changed, 77 insertions(+) create mode 100644 example/lib/examples/activity_handler.dart diff --git a/example/lib/examples/activity_handler.dart b/example/lib/examples/activity_handler.dart new file mode 100644 index 0000000..76159bf --- /dev/null +++ b/example/lib/examples/activity_handler.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +import '../common.dart'; + +class ActivityHandlerExample extends StatefulWidget { + const ActivityHandlerExample({ + Key? key, + }) : super(key: key); + + @override + State createState() => _ActivityHandlerExampleState(); +} + +class _ActivityHandlerExampleState extends State { + int pinnedHeaderIndex = 0; + + @override + Widget build(BuildContext context) { + return AppScaffold( + reverse: false, + title: 'Header #$pinnedHeaderIndex is pinned', + slivers: [ + _StickyHeaderList(index: 0, onHeaderPinned: onHeaderPinned), + _StickyHeaderList(index: 1, onHeaderPinned: onHeaderPinned), + _StickyHeaderList(index: 2, onHeaderPinned: onHeaderPinned), + _StickyHeaderList(index: 3, onHeaderPinned: onHeaderPinned), + ], + ); + } + + void onHeaderPinned(int index) { + setState(() { + pinnedHeaderIndex = index; + }); + } +} + +class _StickyHeaderList extends StatelessWidget { + const _StickyHeaderList({ + Key? key, + this.index, + required this.onHeaderPinned, + }) : super(key: key); + + final int? index; + final void Function(int index) onHeaderPinned; + + @override + Widget build(BuildContext context) { + return SliverStickyHeader( + activityHandler: (activity) { + debugPrint("[Header#$index] $activity"); + if (activity == SliverStickyHeaderActivity.pinned) { + onHeaderPinned(index!); + } + }, + header: Header(index: index), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => ListTile( + leading: CircleAvatar( + child: Text('$index'), + ), + title: Text('List tile #$i'), + ), + childCount: 6, + ), + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 339dff2..d58cd2b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:example/examples/activity_handler.dart'; import 'package:example/examples/nested.dart'; import 'package:flutter/material.dart'; @@ -65,6 +66,10 @@ class _Home extends StatelessWidget { text: 'Reverse List Example', builder: (_) => const ReverseExample(), ), + _Item( + text: 'Activity Header Example', + builder: (_) => const ActivityHandlerExample(), + ), _Item( text: 'Mixing other slivers', builder: (_) => const MixSliversExample(), From 380fdec3cf2eeed193b6de523d44dafdb5d99f6a Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Sun, 25 Jun 2023 19:50:42 +0900 Subject: [PATCH 05/11] Add doc comments --- lib/src/widgets/sliver_sticky_header.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index b34d068..8191e92 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -134,13 +134,23 @@ class SliverStickyHeaderState { } } +/// A callback that handles a [SliverStickyHeaderActivity]. typedef SliverStickyHeaderActivityHandler = void Function( SliverStickyHeaderActivity activity); +/// An event that is dispatched when a sticky header changes its position meaningfully. enum SliverStickyHeaderActivity { + /// Dispatched when the sticky header is completely pushed + /// out of the viewport by the subsequent header. pushed, + + /// Dispatched when the sticky header is unpinned. unpinned, + + /// Dispatched when the sticky header begin to be pinned. pinned, + + /// Dispatched when the sticky header begins to be pushed by the subsequent header. settling, } @@ -216,6 +226,7 @@ class SliverStickyHeader extends RenderObjectWidget { /// will be used. final StickyHeaderController? controller; + /// A callback invoked when a [SliverStickyHeaderActivity] is dispatched. final SliverStickyHeaderActivityHandler? activityHandler; @override From 7ec6fa0c66486a7a86420273b2e0e8bf352ba27b Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Sun, 25 Jun 2023 19:57:35 +0900 Subject: [PATCH 06/11] Rename a local variable --- lib/src/rendering/sliver_sticky_header.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 0faf644..7265e44 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -309,29 +309,29 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { } void _tryNotifyActivity(double headerScrollRatio) { - final SliverStickyHeaderActivity location; + final SliverStickyHeaderActivity activity; if (_isPinned) { if (headerScrollRatio >= 1) { - location = SliverStickyHeaderActivity.pushed; + activity = SliverStickyHeaderActivity.pushed; } else if (headerScrollRatio > 0) { - location = SliverStickyHeaderActivity.settling; + activity = SliverStickyHeaderActivity.settling; } else { - location = SliverStickyHeaderActivity.pinned; + activity = SliverStickyHeaderActivity.pinned; } } else { - location = SliverStickyHeaderActivity.unpinned; + activity = SliverStickyHeaderActivity.unpinned; } if (activityHandler != null && _lastReportedActivity != null && - location != _lastReportedActivity) { + activity != _lastReportedActivity) { WidgetsBinding.instance.scheduleTask( - () => activityHandler?.call(location), + () => activityHandler?.call(activity), Priority.touch, ); } - _lastReportedActivity = location; + _lastReportedActivity = activity; } @override From b6fb407308767f4af46c165c386ea369ac1d8c79 Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Sun, 25 Jun 2023 20:22:04 +0900 Subject: [PATCH 07/11] typo --- example/lib/main.dart | 2 +- lib/src/widgets/sliver_sticky_header.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index d58cd2b..0dc0f86 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -67,7 +67,7 @@ class _Home extends StatelessWidget { builder: (_) => const ReverseExample(), ), _Item( - text: 'Activity Header Example', + text: 'Activity Handler Example', builder: (_) => const ActivityHandlerExample(), ), _Item( diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index 8191e92..127ebd2 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -147,7 +147,7 @@ enum SliverStickyHeaderActivity { /// Dispatched when the sticky header is unpinned. unpinned, - /// Dispatched when the sticky header begin to be pinned. + /// Dispatched when the sticky header begins to be pinned. pinned, /// Dispatched when the sticky header begins to be pushed by the subsequent header. From 4f95c64910fbf8ec4c112d91a66cd55a4ece2dbc Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Sun, 25 Jun 2023 21:53:20 +0900 Subject: [PATCH 08/11] Improve doc comments --- lib/src/widgets/sliver_sticky_header.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index 127ebd2..c74e09e 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -150,7 +150,9 @@ enum SliverStickyHeaderActivity { /// Dispatched when the sticky header begins to be pinned. pinned, - /// Dispatched when the sticky header begins to be pushed by the subsequent header. + /// Dispatched when the [pushed] sticky header begins to push off the + /// currently pinned header, or the [pinned] sticky header begins to + /// be pushed off by the subsequent header. settling, } From 2752f38d8721d2668494d9f17a84e7d6eb63d918 Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Sun, 25 Jun 2023 22:15:58 +0900 Subject: [PATCH 09/11] Improve the doc comments --- lib/src/widgets/sliver_sticky_header.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index c74e09e..7dcda8b 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -140,14 +140,14 @@ typedef SliverStickyHeaderActivityHandler = void Function( /// An event that is dispatched when a sticky header changes its position meaningfully. enum SliverStickyHeaderActivity { - /// Dispatched when the sticky header is completely pushed + /// Dispatched when the [settling] sticky header is completely pushed /// out of the viewport by the subsequent header. pushed, - /// Dispatched when the sticky header is unpinned. + /// Dispatched when the [pinned] sticky header is unpinned. unpinned, - /// Dispatched when the sticky header begins to be pinned. + /// Dispatched when the sticky header is pinned. pinned, /// Dispatched when the [pushed] sticky header begins to push off the From 4c62f5146c0353040b536c262d948f311c974e3d Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Sun, 25 Jun 2023 22:40:21 +0900 Subject: [PATCH 10/11] Rename a method --- lib/src/rendering/sliver_sticky_header.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 7265e44..08f420e 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -267,7 +267,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { constraints.precedingScrollExtent; } - _tryNotifyActivity(headerScrollRatio); + _updateActivity(headerScrollRatio); // second layout if scroll percentage changed and header is a // RenderStickyHeaderLayoutBuilder. @@ -308,7 +308,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { } } - void _tryNotifyActivity(double headerScrollRatio) { + void _updateActivity(double headerScrollRatio) { final SliverStickyHeaderActivity activity; if (_isPinned) { if (headerScrollRatio >= 1) { From d7a95df54208c2a3506ede13b7f96c728b49f2fc Mon Sep 17 00:00:00 2001 From: fujidaiti Date: Thu, 29 Jun 2023 02:03:22 +0900 Subject: [PATCH 11/11] Refactor nested if-else --- lib/src/rendering/sliver_sticky_header.dart | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 08f420e..5700eb1 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -310,16 +310,14 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { void _updateActivity(double headerScrollRatio) { final SliverStickyHeaderActivity activity; - if (_isPinned) { - if (headerScrollRatio >= 1) { - activity = SliverStickyHeaderActivity.pushed; - } else if (headerScrollRatio > 0) { - activity = SliverStickyHeaderActivity.settling; - } else { - activity = SliverStickyHeaderActivity.pinned; - } - } else { + if (!_isPinned) { activity = SliverStickyHeaderActivity.unpinned; + } else if (headerScrollRatio >= 1.0) { + activity = SliverStickyHeaderActivity.pushed; + } else if (headerScrollRatio > 0.0) { + activity = SliverStickyHeaderActivity.settling; + } else { + activity = SliverStickyHeaderActivity.pinned; } if (activityHandler != null &&