Mastering Flutter: Custom Scrollview

In this tutorial you will learn how to achieve custom scrolling effects with the CustomScrollView widget and its sliver. Custom scroll views are particularly useful if you want to develop particular scrolling effects or if you want to have more control on the scrolling content of your ScrollView.

So, what is a Sliver? From the documentation:

A sliver is a portion of a scrollable area. You can use slivers to achieve custom scrolling effects.

Slivers are particular widgets that must produce RenderSliver objects and are the children of the CustomScrollView.

Both ListViews and GridViews uses a CustomScrollView and slivers widgets under the hood.

Here’s some useful Sliver provided by Flutter:

  • SliverAppBar: this sliver renders an app bar, it is typically used as the first child of the custom scroll. It can be configured to change its height according to the scrolling of the user.
  • SliverGrid: a sliver that renders a grid of widgets
  • SliverList and SliverFixedExtentList: slivers that render a list inside the scrollview. The fixed extent version of the list is more efficient because it doesn’t need to calculate the height/width of its children
  • SliverToBoxAdapter: a sliver that contains a box widget, it’s useful if you want to embed a widget inside a custom scroll view

  • SliverPadding: a sliver that applies padding to another sliver.

Let’s build our first custom scroll view:

CustomScrollView(
        slivers: <Widget>[
          const SliverAppBar(
            pinned: true,
            expandedHeight: 250.0,
            flexibleSpace: FlexibleSpaceBar(
              title: Text('Custom Scroll View'),
            ),
          ),
          SliverGrid(
            gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
              maxCrossAxisExtent: 200.0,
              mainAxisSpacing: 10.0,
              crossAxisSpacing: 10.0,
              childAspectRatio: 4.0,
            ),
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return Container(
                  alignment: Alignment.center,
                  color: Colors.amber,
                  child: Text('$index'),
                );
              },
              childCount: 20,
            ),
          ),
          SliverFixedExtentList(
            itemExtent: 50.0,
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return Container(
                  alignment: Alignment.center,
                  child: Text(' $index'),
                );
              },
            ),
          )

This is a simple scrollview with:

  • An app bar at the top
  • A grid 
  • A List

And the result is the following:

NotificationListener and ScrollNotification

Other widgets particularly useful when using custom scroll views are NotificationListener ScrollNotification, you can use this widget to receive events when the user scrolls without using a ScrollController.

Let’s wrap our CustomScrollView inside a NotificationListener:

NotificationListener<ScrollNotification>(
        onNotification: (notification) {
          print(notification);
          return true;
        },
        child: CustomScrollView(
...
)

Now whenever the user interacts with the scroll view the onNotification callback will be invoked with a ScrollNotification, you can use the information of the notification to change the layout of the widgets, or to manage your data.
You could load more data when the user reaches the end of the content of the scroll view). 
Let’s implement it.

          onNotification: (notification) {
            if (notification is ScrollEndNotification) {
              // We use 20 as our threshold to load more data
              if (notification.metrics.extentAfter < 20) {
                print('Load more data');
              }
            }
            return true;
          },

If you wish to check out more awesome tutorials click here!

Add a Comment

Your email address will not be published. Required fields are marked *