NestedScrollView class
A scrolling view inside of which can be nested other scrolling views, with their scroll positions being intrinsically linked.
The most common use case for this widget is a scrollable view with a flexible SliverAppBar containing a TabBar in the header (built by headerSliverBuilder), and with a TabBarView in the body, such that the scrollable view's contents vary based on which tab is visible.
Motivation
In a normal ScrollView, there is one set of slivers (the components of the scrolling view). If one of those slivers hosted a TabBarView which scrolls in the opposite direction (e.g. allowing the user to swipe horizontally between the pages represented by the tabs, while the list scrolls vertically), then any list inside that TabBarView would not interact with the outer ScrollView. For example, flinging the inner list to scroll to the top would not cause a collapsed SliverAppBar in the outer ScrollView to expand.
NestedScrollView solves this problem by providing custom ScrollControllers for the outer ScrollView and the inner ScrollViews (those inside the TabBarView, hooking them together so that they appear, to the user, as one coherent scroll view.
To create a local project with this code sample, run:
flutter create --sample=widgets.NestedScrollView.1 mysample
SliverAppBars with NestedScrollViews
Using a SliverAppBar in the outer scroll view, or headerSliverBuilder, of a NestedScrollView may require special configurations in order to work as it would if the outer and inner were one single scroll view, like a CustomScrollView.
Pinned SliverAppBars
A pinned SliverAppBar works in a NestedScrollView exactly as it would in another scroll view, like CustomScrollView. When using SliverAppBar.pinned, the app bar remains visible at the top of the scroll view. The app bar can still expand and contract as the user scrolls, but it will remain visible rather than being scrolled out of view.
This works naturally in a NestedScrollView, as the pinned SliverAppBar is not expected to move in or out of the visible portion of the viewport. As the inner or outer Scrollables are moved, the app bar persists as expected.
If the app bar is floating, pinned, and using an expanded height, follow the floating convention laid out below.
Floating SliverAppBars
When placed in the outer scrollable, or the headerSliverBuilder, a SliverAppBar that floats, using SliverAppBar.floating will not be triggered to float over the inner scroll view, or body, automatically.
This is because a floating app bar uses the scroll offset of its own Scrollable to dictate the floating action. Being two separate inner and outer Scrollables, a SliverAppBar in the outer header is not aware of changes in the scroll offset of the inner body.
In order to float the outer, use NestedScrollView.floatHeaderSlivers. When set to true, the nested scrolling coordinator will prioritize floating in the header slivers before applying the remaining drag to the body.
Furthermore, the floatHeaderSlivers
flag should also be used when using an
app bar that is floating, pinned, and has an expanded height. In this
configuration, the flexible space of the app bar will open and collapse,
while the primary portion of the app bar remains pinned.
To create a local project with this code sample, run:
flutter create --sample=widgets.NestedScrollView.2 mysample
Snapping SliverAppBars
Floating SliverAppBars also have the option to perform a snapping animation. If SliverAppBar.snap is true, then a scroll that exposes the floating app bar will trigger an animation that slides the entire app bar into view. Similarly if a scroll dismisses the app bar, the animation will slide the app bar completely out of view.
It is possible with a NestedScrollView to perform just the snapping animation without floating the app bar in and out. By not using the NestedScrollView.floatHeaderSlivers, the app bar will snap in and out without floating.
The SliverAppBar.snap animation should be used in conjunction with the SliverOverlapAbsorber and SliverOverlapInjector widgets when implemented in a NestedScrollView. These widgets take any overlapping behavior of the SliverAppBar in the header and redirect it to the SliverOverlapInjector in the body. If it is missing, then it is possible for the nested "inner" scroll view below to end up under the SliverAppBar even when the inner scroll view thinks it has not been scrolled.
To create a local project with this code sample, run:
flutter create --sample=widgets.NestedScrollView.3 mysample
Snapping and Floating SliverAppBars
Currently, NestedScrollView does not support simultaneously floating and snapping the outer scrollable, e.g. when using SliverAppBar.floating & SliverAppBar.snap at the same time.
Stretching SliverAppBars
Currently, NestedScrollView does not support stretching the outer scrollable, e.g. when using SliverAppBar.stretch.
See also:
- SliverAppBar, for examples on different configurations like floating, pinned and snap behaviors.
- SliverOverlapAbsorber, a sliver that wraps another, forcing its layout extent to be treated as overlap.
- SliverOverlapInjector, a sliver that has a sliver geometry based on the values stored in a SliverOverlapAbsorberHandle.
- Inheritance
-
- Object
- DiagnosticableTree
- Widget
- StatefulWidget
- NestedScrollView
Constructors
- NestedScrollView({Key? key, ScrollController? controller, Axis scrollDirection = Axis.vertical, bool reverse = false, ScrollPhysics? physics, required NestedScrollViewHeaderSliversBuilder headerSliverBuilder, required Widget body, DragStartBehavior dragStartBehavior = DragStartBehavior.start, bool floatHeaderSlivers = false, Clip clipBehavior = Clip.hardEdge, HitTestBehavior hitTestBehavior = HitTestBehavior.opaque, String? restorationId, ScrollBehavior? scrollBehavior})
-
Creates a nested scroll view.
const
Properties
- body → Widget
-
The widget to show inside the NestedScrollView.
final
- clipBehavior → Clip
-
The content will be clipped (or not) according to this option.
final
- controller → ScrollController?
-
An object that can be used to control the position to which the outer
scroll view is scrolled.
final
- dragStartBehavior → DragStartBehavior
-
Determines the way that drag start behavior is handled.
final
- floatHeaderSlivers → bool
-
Whether or not the NestedScrollView's coordinator should prioritize the
outer scrollable over the inner when scrolling back.
final
- hashCode → int
-
The hash code for this object.
no setterinherited
- headerSliverBuilder → NestedScrollViewHeaderSliversBuilder
-
A builder for any widgets that are to precede the inner scroll views (as
given by body).
final
- hitTestBehavior → HitTestBehavior
-
Defines the behavior of gesture detector used in this Scrollable.
final
- key → Key?
-
Controls how one widget replaces another widget in the tree.
finalinherited
- physics → ScrollPhysics?
-
How the scroll view should respond to user input.
final
- restorationId → String?
-
Restoration ID to save and restore the scroll offset of the scrollable.
final
- reverse → bool
-
Whether the scroll view scrolls in the reading direction.
final
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
- scrollBehavior → ScrollBehavior?
-
A ScrollBehavior that will be applied to this widget individually.
final
- scrollDirection → Axis
-
The Axis along which the scroll view's offset increases.
final
Methods
-
createElement(
) → StatefulElement -
Creates a StatefulElement to manage this widget's location in the tree.
inherited
-
createState(
) → NestedScrollViewState -
Creates the mutable state for this widget at a given location in the tree.
override
-
debugDescribeChildren(
) → List< DiagnosticsNode> -
Returns a list of DiagnosticsNode objects describing this node's
children.
inherited
-
debugFillProperties(
DiagnosticPropertiesBuilder properties) → void -
Add additional properties associated with the node.
inherited
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
toDiagnosticsNode(
{String? name, DiagnosticsTreeStyle? style}) → DiagnosticsNode -
Returns a debug representation of the object that is used by debugging
tools and by DiagnosticsNode.toStringDeep.
inherited
-
toString(
{DiagnosticLevel minLevel = DiagnosticLevel.info}) → String -
A string representation of this object.
inherited
-
toStringDeep(
{String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) → String -
Returns a string representation of this node and its descendants.
inherited
-
toStringShallow(
{String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) → String -
Returns a one-line detailed description of the object.
inherited
-
toStringShort(
) → String -
A short, textual description of this widget.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited
Static Methods
-
sliverOverlapAbsorberHandleFor(
BuildContext context) → SliverOverlapAbsorberHandle - Returns the SliverOverlapAbsorberHandle of the nearest ancestor NestedScrollView.