diff --git a/README.md b/README.md index ab23f3c..5aaeea0 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ NestedScrollView: extended nested scroll view to fix following issues. 4.do without ScrollController in NestedScrollView's body +5.[Unable to stretch SliverAppBar issue](https://github.com/flutter/flutter/issues/54059) + [Web demo for ExtendedNestedScrollView](https://fluttercandies.github.io/extended_nested_scroll_view/) - [extended_nested_scroll_view](#extended_nested_scroll_view) @@ -23,6 +25,7 @@ NestedScrollView: extended nested scroll view to fix following issues. - [Step2](#step2) - [Example for NestedScrollView pull to refresh](#example-for-nestedscrollview-pull-to-refresh) - [Do without ScrollController in NestedScrollView's body](#do-without-scrollcontroller-in-nestedscrollviews-body) + # Example for issue 1 give total height of pinned sliver headers in pinnedHeaderSliverHeightBuilder callback @@ -38,7 +41,7 @@ give total height of pinned sliver headers in pinnedHeaderSliverHeightBuilder ca pinnedHeaderSliverHeightBuilder: () { return pinnedHeaderHeight; }, - + ``` # Example for issue 2 @@ -87,6 +90,37 @@ NestedScrollViewRefreshIndicator is as the same as Flutter RefreshIndicator. }, ``` +# Example for NestedScrollView to stretch SliverAppBar + +Just set `stretchHeaderSlivers` to `true`. Be sure to set both `NestedScrollView` and the body ScrollView physics to `const BouncingScrollPhysics(parent: const AlwaysScrollableScrollPhysics())`. See [scroll to top](https://github.com/fluttercandies/extended_nested_scroll_view/tree/master/example/lib/pages/scroll_to_top.dart) for the full example. + +This is a quick hack, optimal effect requires extensive refactoring, thus official Flutter team is not picking up this issue. Some limitations apply: +1. The body scroll view must have `BouncingScrollPhysics`, when the SliverAppBar background stretches, so does the body scroll view. +2. SliverAppBar and body scroll physics does not connect seamlessly. As a result, the SliverAppBar won't stretch by carried momentum (when you quick fling down then not touching the screen), your fingertip has to be touching the screen when stretching the SliverAppBar. + +```dart +NestedScrollView( + // [SliverAppBar.stretch not supported issue](https://github.com/flutter/flutter/issues/54059) + stretchHeaderSlivers: true, + physics: const BouncingScrollPhysics(parent: const AlwaysScrollableScrollPhysics()), // Imoprtant + headerSliverBuilder: (BuildContext c, bool f) { + return [ + SliverAppBar( + pinned: true, + expandedHeight: 200.0, + stretch: true, + stretchTriggerOffset: 1.0, + flexibleSpace: FlexibleSpaceBar( + collapseMode: CollapseMode.pin, + stretchModes: [StretchMode.blurBackground, StretchMode.zoomBackground], + background: Image.asset( + 'assets/467141054.jpg', + fit: BoxFit.cover, + ))) + ]; + }, +``` + [Better one to pull to refresh](https://github.com/fluttercandies/loading_more_list/blob/master/example/lib/demo/nested_scroll_view_demo.dart) Please see the example app of this for a full example. @@ -94,17 +128,16 @@ Please see the example app of this for a full example. # Do without ScrollController in NestedScrollView's body * due to we can't set ScrollController for list in NestedScrollView's body(it will breaking behaviours of InnerScrollController in NestedScrollView),provide Demos - + * [pull to refresh](https://github.com/fluttercandies/extended_nested_scroll_view/tree/master/example/lib/pages/pull_to_refresh.dart), - -* [load more](https://github.com/fluttercandies/extended_nested_scroll_view/tree/master/example/lib/pages/load_more.dart) - -* [scroll to top](https://github.com/fluttercandies/extended_nested_scroll_view/tree/master/example/lib/pages/scroll_to_top.dart) - + +* [load more](https://github.com/fluttercandies/extended_nested_scroll_view/tree/master/example/lib/pages/load_more.dart) + +* [scroll to top](https://github.com/fluttercandies/extended_nested_scroll_view/tree/master/example/lib/pages/scroll_to_top.dart) + to show how to do it without ScrollController -* [pinned header height](https://github.com/fluttercandies/extended_nested_scroll_view/tree/master/example/lib/pages/dynamic_pinned_header_height.dart) +* [pinned header height](https://github.com/fluttercandies/extended_nested_scroll_view/tree/master/example/lib/pages/dynamic_pinned_header_height.dart) to show how to change pinned header height dynamically. - diff --git a/example/lib/pages/complex/scroll_to_top.dart b/example/lib/pages/complex/scroll_to_top.dart index e4573a2..cce8e95 100644 --- a/example/lib/pages/complex/scroll_to_top.dart +++ b/example/lib/pages/complex/scroll_to_top.dart @@ -66,18 +66,24 @@ class _ScrollToTopDemoState extends State kToolbarHeight; return NestedScrollView( key: _key, + // [SliverAppBar.stretch not supported issue](https://github.com/flutter/flutter/issues/54059) + stretchHeaderSlivers: true, + physics: const BouncingScrollPhysics(parent: const AlwaysScrollableScrollPhysics()), headerSliverBuilder: (BuildContext c, bool f) { return [ SliverAppBar( pinned: true, expandedHeight: 200.0, + stretch: true, + stretchTriggerOffset: 1.0, title: const Text('scroll to top'), flexibleSpace: FlexibleSpaceBar( //centerTitle: true, collapseMode: CollapseMode.pin, + stretchModes: [StretchMode.blurBackground, StretchMode.zoomBackground], background: Image.asset( 'assets/467141054.jpg', - fit: BoxFit.fill, + fit: BoxFit.cover, ))) ]; }, @@ -116,9 +122,9 @@ class _ScrollToTopDemoState extends State const Key('Tab0'), GlowNotificationWidget( ListView.builder( + physics: const BouncingScrollPhysics(parent: const AlwaysScrollableScrollPhysics()), //store Page state key: const PageStorageKey('Tab0'), - physics: const ClampingScrollPhysics(), itemBuilder: (BuildContext c, int i) { return Container( alignment: Alignment.center, @@ -138,7 +144,7 @@ class _ScrollToTopDemoState extends State ListView.builder( //store Page state key: const PageStorageKey('Tab1'), - physics: const ClampingScrollPhysics(), + physics: const BouncingScrollPhysics(parent: const AlwaysScrollableScrollPhysics()), itemBuilder: (BuildContext c, int i) { return Container( alignment: Alignment.center, diff --git a/lib/src/old_extended_nested_scroll_view.dart b/lib/src/old_extended_nested_scroll_view.dart index 624e940..4cf937e 100644 --- a/lib/src/old_extended_nested_scroll_view.dart +++ b/lib/src/old_extended_nested_scroll_view.dart @@ -191,6 +191,7 @@ class NestedScrollView extends StatefulWidget { required this.body, this.dragStartBehavior = DragStartBehavior.start, this.floatHeaderSlivers = false, + this.stretchHeaderSlivers = false, this.clipBehavior = Clip.hardEdge, this.restorationId, this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, @@ -278,6 +279,9 @@ class NestedScrollView extends StatefulWidget { /// is expected to float. This cannot be null. final bool floatHeaderSlivers; + /// Whether or not the [NestedScrollView] has a [SliverAppBar] that is expected to stretch on overscroll. + final bool stretchHeaderSlivers; + /// {@macro flutter.widgets.Clip} /// /// Defaults to [Clip.hardEdge]. @@ -372,6 +376,7 @@ class NestedScrollViewState extends State { widget.pinnedHeaderSliverHeightBuilder, widget.innerScrollPositionKeyBuilder, widget.floatHeaderSlivers, + widget.stretchHeaderSlivers, ); } @@ -565,6 +570,7 @@ class _NestedScrollCoordinator this.pinnedHeaderSliverHeightBuilder, this.innerScrollPositionKeyBuilder, this._floatHeaderSlivers, + this._stretchHeaderSlivers, ) { final double initialScrollOffset = _parent?.initialScrollOffset ?? 0.0; _outerController = _NestedScrollController(this, @@ -586,6 +592,7 @@ class _NestedScrollCoordinator ScrollController? _parent; final VoidCallback _onHasScrolledBodyChanged; final bool _floatHeaderSlivers; + final bool _stretchHeaderSlivers; late _NestedScrollController _outerController; late _NestedScrollController _innerController; @@ -723,7 +730,8 @@ class _NestedScrollCoordinator } } - if (innerPosition == null) { + // Kenshin: if innser scrollview is scrolled beyond top, change _outerPosition only + if (innerPosition == null || (_stretchHeaderSlivers && innerPosition.pixels == 0.0)) { // It's either just us or a velocity=0 situation. return _outerPosition!.createBallisticScrollActivity( _outerPosition!.physics @@ -751,6 +759,7 @@ class _NestedScrollCoordinator ), mode: _NestedBallisticScrollActivityMode.inner, ); + } _NestedScrollMetrics _getMetrics( @@ -783,6 +792,7 @@ class _NestedScrollCoordinator // This handles going forward (fling up) and inner list is scrolled past // zero. We want to grab the extra pixels immediately to shrink. extra = _outerPosition!.maxScrollExtent - _outerPosition!.pixels; + extra = extra > 0 ? extra : 0; assert(extra >= 0.0); minRange = pixels; maxRange = pixels + extra; @@ -793,6 +803,7 @@ class _NestedScrollCoordinator // This handles going backward (fling down) and inner list is // underscrolled. We want to grab the extra pixels immediately to grow. extra = _outerPosition!.pixels - _outerPosition!.minScrollExtent; + extra = extra > 0 ? extra : 0; assert(extra >= 0.0); minRange = pixels - extra; maxRange = pixels; @@ -814,6 +825,7 @@ class _NestedScrollCoordinator (_outerPosition!.maxScrollExtent - _outerPosition!.minScrollExtent); } + extra = extra < 0 ? extra : 0; assert(extra <= 0.0); minRange = _outerPosition!.minScrollExtent; maxRange = _outerPosition!.maxScrollExtent + extra; @@ -1058,8 +1070,15 @@ class _NestedScrollCoordinator outerDelta = math.max(outerDelta, overscroll); overscrolls.add(overscroll); } - if (outerDelta != 0.0) - outerDelta -= _outerPosition!.applyClampedDragUpdate(outerDelta); + if (outerDelta != 0.0) { + if (_stretchHeaderSlivers) { + // Kenshin: if has stretch header, let _outerPosition consume all scroll delta; + // otherwise, it will ba clamped and the innerPostion will consume the remaining delta + _outerPosition!.applyFullDragUpdate(outerDelta); + } else { + outerDelta -= _outerPosition!.applyClampedDragUpdate(outerDelta); + } + } // Now deal with any overscroll // TODO(Piinks): Configure which scrollable receives overscroll to