Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Sliverappbar stretch effect #73

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 42 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -38,7 +41,7 @@ give total height of pinned sliver headers in pinnedHeaderSliverHeightBuilder ca
pinnedHeaderSliverHeightBuilder: () {
return pinnedHeaderHeight;
},

```
# Example for issue 2

Expand Down Expand Up @@ -87,24 +90,54 @@ 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 <Widget>[
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.

# 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.

12 changes: 9 additions & 3 deletions example/lib/pages/complex/scroll_to_top.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,24 @@ class _ScrollToTopDemoState extends State<ScrollToTopDemo>
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 <Widget>[
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,
)))
];
},
Expand Down Expand Up @@ -116,9 +122,9 @@ class _ScrollToTopDemoState extends State<ScrollToTopDemo>
const Key('Tab0'),
GlowNotificationWidget(
ListView.builder(
physics: const BouncingScrollPhysics(parent: const AlwaysScrollableScrollPhysics()),
//store Page state
key: const PageStorageKey<String>('Tab0'),
physics: const ClampingScrollPhysics(),
itemBuilder: (BuildContext c, int i) {
return Container(
alignment: Alignment.center,
Expand All @@ -138,7 +144,7 @@ class _ScrollToTopDemoState extends State<ScrollToTopDemo>
ListView.builder(
//store Page state
key: const PageStorageKey<String>('Tab1'),
physics: const ClampingScrollPhysics(),
physics: const BouncingScrollPhysics(parent: const AlwaysScrollableScrollPhysics()),
itemBuilder: (BuildContext c, int i) {
return Container(
alignment: Alignment.center,
Expand Down
25 changes: 22 additions & 3 deletions lib/src/old_extended_nested_scroll_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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].
Expand Down Expand Up @@ -372,6 +376,7 @@ class NestedScrollViewState extends State<NestedScrollView> {
widget.pinnedHeaderSliverHeightBuilder,
widget.innerScrollPositionKeyBuilder,
widget.floatHeaderSlivers,
widget.stretchHeaderSlivers,
);
}

Expand Down Expand Up @@ -565,6 +570,7 @@ class _NestedScrollCoordinator
this.pinnedHeaderSliverHeightBuilder,
this.innerScrollPositionKeyBuilder,
this._floatHeaderSlivers,
this._stretchHeaderSlivers,
) {
final double initialScrollOffset = _parent?.initialScrollOffset ?? 0.0;
_outerController = _NestedScrollController(this,
Expand All @@ -586,6 +592,7 @@ class _NestedScrollCoordinator
ScrollController? _parent;
final VoidCallback _onHasScrolledBodyChanged;
final bool _floatHeaderSlivers;
final bool _stretchHeaderSlivers;

late _NestedScrollController _outerController;
late _NestedScrollController _innerController;
Expand Down Expand Up @@ -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)) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be innerPosition.pixels <= 0.0, if use equal, sometimes, inner scrollview is not scroll to top

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

// It's either just us or a velocity=0 situation.
return _outerPosition!.createBallisticScrollActivity(
_outerPosition!.physics
Expand Down Expand Up @@ -751,6 +759,7 @@ class _NestedScrollCoordinator
),
mode: _NestedBallisticScrollActivityMode.inner,
);

}

_NestedScrollMetrics _getMetrics(
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down