Skip to main content
Flutter

A Guide to NestedScrollView

What Is NestedScrollView?

When an app page contains scrollable widgets, maximizing the usable scrolling area can improve the experience. NestedScrollView coordinates multiple scrollable widgets so that an outer header and inner content can scroll together. A common use case combines a SliverAppBar with a tab view.

Basic Usage

To use NestedScrollView, pair SliverOverlapAbsorber with SliverOverlapInjector. The absorber records the overlap produced by the header, and the injector applies the corresponding space inside the inner scroll view.

NestedScrollView(
    headerSliverBuilder:
        (BuildContext context, bool innerBoxIsScrolled) {
      return <Widget>[
        SliverOverlapAbsorber(
          handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
          sliver: SliverAppBar(
            title: const Text('Snapping Nested SliverAppBar'),
            floating: true,
            snap: true,
            expandedHeight: 200.0,
            forceElevated: innerBoxIsScrolled,
          ),
        ),
      ];
    },
    body: Builder(builder: (BuildContext context) {
      return CustomScrollView(
        slivers: <Widget>[
          SliverOverlapInjector(
              handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context)),
        ],
    },
)

Using a TabView

NestedScrollView(
    headerSliverBuilder:
        (BuildContext context, bool innerBoxIsScrolled) {
      return <Widget>[
        SliverOverlapAbsorber(
          handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
          sliver: SliverAppBar(
            title: const Text('Snapping Nested SliverAppBar'),
            floating: true,
            snap: true,
            expandedHeight: 200.0,
            forceElevated: innerBoxIsScrolled,
            bottom: TabBar(
                tabs: [
                    ...
                ]
            )
          ),
        ),
      ];
    },
    body: TabBarView(
        children: [
        Builder(builder: (BuildContext context) {
      return CustomScrollView(
        slivers: <Widget>[
          SliverOverlapInjector(
              handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context)),
        ],
    },],),
)

Using RefreshIndicator

When using RefreshIndicator inside NestedScrollView, set notificationPredicate; otherwise, the refresh indicator may fail to appear.

 RefreshIndicator(
        onRefresh: () async {
            ...
        },
        notificationPredicate: (_) {
            return true;
            },
        )

Customizing the Header

You can modify the FlexibleSpaceBar implementation to change the header's appearance while scrolling.

if (widget.stretchModes.contains(StretchMode.fadeTitle) &&
    constraints.maxHeight > settings.maxExtent) {
final double stretchOpacity = 1 -
    clampDouble((constraints.maxHeight - settings.maxExtent) / 100,
        0.0, 1.0);
title = Opacity(
    opacity: stretchOpacity,
    child: title,
);
}