An in-depth look at Flutter’s CustomScrollView Widget

Having possibly read my previous article, Decode ListView, you may have noticed I make no mention of an additional ‘scrollable’ widget that also extends ListView’s base class, ScrollView. It’s called, CustomScrollView. And with it, you’re privy to even more scrolling effects.

Since a number of other ‘Sliver’ widgets are involved with CustomScrollView, we’ll dedicate this article to them. Unlike ListView class, you’re to supply ‘Sliver’ widgets directly to the CustomScrollView thus allowing for these additional scrolling effects.

A Sliver contains Widgets which will makes up the scrollable area. The ‘visible’ portion of which is called the viewport.

You can see above, in the gif file above on the left, a ‘floating app bar’ is one of these effects available to you. Expanding or compressing depending on the direction. In the second example, you see that a ‘gridview’ and ‘listview’ can be incorporated together making for another possible scrolling effect. Notice further how the title word, ‘Demo’, moves and resizes as the scrolling occurs.

I Like Screenshots. Click For Gists.

As always, I prefer using screenshots over gists to show concepts rather than just show code in my articles. I find them easier to work with frankly. However, you can click or tap on these screenshots to see the code they represent in a gist or in Github. Ironically, it’s better to read this article about mobile development on your computer than on your phone. Besides, we program on our computers — not on our phones. For now.

No Moving Pictures On Social Media

Note, there are a number of gif files in this article demonstrating the actual scrolling of items in emulators. However, it’s said viewing such files is not possible when reading this article on platforms like Instagram, Facebook, etc. All you’ll see are empty square spaces where the gif files should be. I wanted you to be aware of this, and recommend reading this on medium.com.

Let’s begin.

Image for post
Image for post
Other Stories by Greg Perry

Learn By Example Of Course

Again and again, I try to use established examples provided directly by Google to demonstrate the subject matter at hand. In this case, we’ve turned once again to the Flutter Cookbook for an example of the CustomScrollView. This one‘s titled, Place a floating app bar above a list. A second example was found on the page describing the CustomScrollView class itself. Both were presented to you at the beginning of this article as those two gif files.

Hide Your App Bar

As explained in the Cookbook example, in many mobile apps there’s an ‘app bar’ displayed across the top. There are times when the app has a very long list of items to scroll through, and so it’s beneficial to hide the app bar while going through such a list. That’s what the first example demonstrates.

Image for post
Image for post
Complete example

A List of Slivers

The widget, CustomScrollView, primary contribution is the means to take in literally a list of ‘Sliver’ widgets with the use of the named parameter, slivers. Above, you see the SliverAppBar widget and the SliverList widget are listed in the first example.

Let’s examine the SliverAppBar widget right now as its a rather important widget used with the CustomScrollView. Let’s review its parameters that contribute to its prolific use.

Image for post
Image for post
SliverAppBar class #

Take The Lead

If you pass a widget to the named parameter, leading, you’ve placed that widget before the app bar’s title. Below, I’ve simply passed in an ‘IconButton’ widget as an example. One that takes you back to the ‘home screen’. On the screen, you then now see the ‘home’ icon appearing before the title.

Image for post
Image for post

Before Then After

To place a number of widgets ‘after’ the app bar’s title, you have the named parameter, actions. It takes in a List of Widgets. Traditionally it’s a list of IconButtons the user then presses to invoke other functions and features of the app. As an example, below I’ve supplied a shopping cart to the app bar.

Image for post
Image for post

Tab The Bottom

The widget that appears across the bottom of the app bar is taken in by the named parameter, bottom. The one constraint being it has to be a widget that implements the class, PreferredSizeWidget. Traditionally, a TabBar class is chosen so to supply a list of tabs across the bottom of the app bar.

In this very simple example above, a TabController object, controller, is used to sync the app bar tabs with the TabBarView’s widgets and display the appropriate Text widget with every press of a tab. Of course, it doesn’t have to be a Text widget but any elaborate widget you can dream up. Don’t mind the two other parameters, pinned and expandedHeight. We’ll get to those shortly.

Let’s Be Flexible!

The named parameter, flexibleSpace, also places a widget in the app bar. However, this widget is stacked behind the title and the tab bar allowing you to place a picture in the background for example. Traditionally, the widget is a FlexibleSpaceBar, such a widget actually expands and collapses when scrolling down and up respectively. Below, an ‘Internet’ image is utilized as a background using the previous TabBar example.

Note, the screenshot above had another named parameter, expandedHeight, set to 200.0. The is the height (in logical pixels) of the app bar when fully extended. Used, in this case, so the image is more clearly visible.

Logical pixels are roughly the same visual size across devices while Physical pixels are the size of the actual hardware pixels on the device. Logical pixels are to be device-independent and resolution-independent pixels.

It Don’t Float!

Setting the SliverAppBar’s property, floating, to false, the app bar will not appear until your scrolling reaches the top as demonstrated below.

However, setting the property, floating, to true, and while scrolling back to the top of the scrollable list, you’ll see the app bar appears right away. As was the case for the first Cookbook example, Place a floating app bar above a list.

Pinned To It

Note, setting the property, pinned, to true, and it won’t matter what the floating setting is — the app bar will always be visible.

Snap n’ Center!

The named parameter, snap, only works if the other parameter, floating, is set to true, and invokes some animation when scrolling is initiated. Beginning the scrolling in either direction will trigger an animation of the app bar in that direction, and it will ‘snap’ either in or out of view depending upon that direction. Note, another named parameter, centerTitle, is also introduced. If set to true, the title is then displayed in the centre of the app bar.

Note, I’m using the parameter, expandedHeight, again making the app bar appear thicker at 200 ‘logical pixels’. It’s only included so to make the ‘snapping’ animation more apparent.

What’s Your Background?

Raouf Rahiche’s SliverAppBar example literally takes a List of appBar objects and utilizes a number of features in the FlexibleSpaceBar class including the background option. I felt it an interesting example to share in this article.

Note, in the example, the CustomScrollView’s parameter, reverse, is set to true. To quickly illustrate the purpose of this parameter. See in the screenshots below how the appBar objects are now at the top, with the reverse option simply commented out. It merely reverses the list of widget items.

Fill In The Rest

Note also, the additional ‘Sliver’ widget called, SliverFillRemaining. It’s to ‘fill-up’ the remaining space if any in the viewport (the ‘visible’ portion of the scrollable area) with the widget specified in the named parameter, child. In Raouf’s example, its an empty Container, but highlighting its border in red and saying, hello, in the centre shows you that indeed it fills up the rest.

Got The Look

Ok, let’s quickly review some of the remaining parameters available to you when using the SliverAppBar widget. Highlighted with little red arrows below, you can see some additional parameters being included in this SliverAppBar example. You can also see the results thereof.

Not particularly pretty, but it quickly gives you an idea of what can be done to the colour of the SliverAppBar icons for example. The font of the title has been increased and the app bar itself is now transparent. Though not readily apparent in the example, the parameter, forceElevated, set to true casts a slight ‘shadow’ at the border of the appbar to create the effect that the appbar is indeed elevated up a bit. Though hard to see in this example.

The Sliver List Listing Slivers

The next widget we’ll examine is the second Sliver widget found in the very first example. The SliverList. As you see below there’s not much to the class itself, and so we’ll not talk much about the SliverList widget itself. However, it does take in a class of type, SliverChildDelegate. That means it can take in either a SliverChildBuilderDelegate object as in the first example or a SliverChildListDelegate object. We’ll talk about these two a little bit.

Image for post
Image for post
SliverList class ^

It’s Delegated

If you’ve read the previous article, Decode ListView, it was there where you were first introduced to these two classes: SliverChildBuilderDelegate and SliverChildListDelegate. However, in this article, we’ve got the SliverList, but it can take in either one of these two classes. And in fact, it can take in any class that extends the class of type, SliverChildDelegate. It’s that class is then delegated to actually create the widgets that will make up a scrollable listing (or Sliver).

Below is the heart of these two classes in particular— their build() functions. On the left is the SliverChildBuilderDelegate, and on the right, is the SliverChildListDelegate. We can see how they differ from one another.

Scrolling Slivers Of Widgets

Each build() function returns a Widget. However, the SliverChildBuilderDelegate’s build() function contains a ‘builder’ routine while the SliverChildListDelegate’s build() function is instead explicitly provided a finite list of already constructed ‘child’ widgets. Both build() functions are provided an index parameter, but for the SliverChildListDelegate’s build() function, that index is merely applied to the list of provided child widgets and picks out one from that list by the index. While there’s more work to be done in the SliverChildListDelegate’s build() function as the index must be passed on to the ‘builder’ routine. For more information, please return to the previous article section called, Three types of Slivers.

Fix The Width

In the first example, since the standard extent (width) of items in a scrollable listings is 50.0 logical pixels, a more efficient approach would be the use the widget, SliverFixedExtentList, instead of the SliverList — since the SliverList has to ‘calculate’ the ‘width of items’ itself when creating its Slivers.

Image for post
Image for post

It appears to be a good habit to use the SliverFixedExtentList instead for a little better performance in your apps.

Make A Grid Scroll

In the second example, found on the CustomScrollView class page itself, we’re introduced to a scrollable two-column grid. It’s called a SliverGrid. The CustomScrollView for the second example is displayed below. Note the SliverFixedExtentList and not the SliverList is also included in this example.

Image for post
Image for post

2D Delegation

Like the SliverList, the SliverGrid takes in a ‘delegate’ (of class of type, SliverChildDelegate) to create the widgets that make up the Slivers, but it must also take in a ‘Grid Delegate.’ A class of type, SliverGridDelegate.

Image for post
Image for post
SliverGrid class ^

Take It To The Max

There are currently two classes that implement the class, SliverGridDelegate. The one used in the second example has a long and self-explanatory name called, SliverGridDelegateWithMaxCrossAxisExtent.

It’s particularly useful if the items vary in length (on the cross axis). With this class, you specify a maximum number of ‘logical pixels’ an item can take up, and it’ll determine how many ‘equally sized’ columns should fit ‘on the cross axis’ depending on the orientation of the grid.

The main axis direction of a grid is the direction in which it scrolls whether that be vertically as in most cases or horizontally; the cross axis direction is the orthogonal direction (at right angle to the main axis direction) .

Image for post
Image for post
Image for post
Image for post

What’s The Ratio

Change the second example’s childAspectRatio from 4.0 to its default, 1.0, and those grid items get a lot bigger. The lower the ratio in relation to the number columns, the larger the width compared to the height of the items.

Image for post
Image for post

It’s Fixed

The other class that implements SliverGridDelegate is also a mouth-full being it’s called, SliverGridDelegateWithFixedCrossAxisCount. It’s given a fixed number as to the number of items to be placed on the cross axis. In the second example, we could have just as easily used this class instead. In fact, it would be more efficient because it doesn’t need to now calculate the column count.

Image for post
Image for post

Grid’s Other Constructors

There are two other constructors available to you when using the SliverGrid. One is the SliverGrid.count and the other is the SliverGrid.extent. There’s a big difference between these two constructors and the original SliverGrid constructor in that there are no ‘delegates’ to pass to them. And so in both, the widgets to be scrolled through are to be already created and provided in a List. That List is then passed to the named parameter, children. See below.

The Same Old Grid Delegates

As for the ‘grid delegates,’ well you’ve seen them before. For example, the last grid delegate we talked about was that long descriptive class called, SliverGridDelegateWithFixedCrossAxisCount. Well, guess what. It’s used in the constructor, SliverGrid.count as well. See below.

Image for post
Image for post

The other constructor describes an ‘extent’ in its name, SliverGrid.extent. Thus, its grid delegate involves the extent (the width) of the items on the cross axis and is called, SliverGridDelegateWithMaxCrossAxisExtent.

Image for post
Image for post

Pad The Scrolling

Using the very first example again, we’ll take a quick look at the widget, SliverPadding. It’s designed to supply padding (empty space) around another Sliver widget. It’s for more specific circumstances than this, but below you’ll see I merely surrounded the SliverList with 32 logical pixels of ‘padding.’ I’ve also included the Card widget to make the padding more apparent.

Adapt Or Don’t

Another ‘Sliver’ widget is the SliverToBoxAdapter. It allows you to introduce ‘self-contained widget’ of any complexity into the scrollable area. Such widgets define their own size and what is called in the documentation as a ‘box widget.’ As you see the example below, again, I’m simply carrying on with the above example, but now introducing a ListView widget that instead scrolls its items vertically, it scrolls its items horizontally. The ListView is contained in a ‘box widget’ (a Container widget) that defines its own size.

Let’s leave it at that for now. The coffee’s getting cold, and you, no doubt, have got things to do. You now know the CustomScrollView widget gives you more options when it comes to scrolling, and we developers all love options.

Cheers

TL;DR It’s Been Done

The rest of the article will not really touch on the rest of the parameters found in the CustomScrollView. That’s because these parameters were already covered in the previous article, Decode ListView, starting at the header labelled, Going Horizontal.

Image for post
Image for post
See Going Horizontal
Image for post
Image for post
See List In Reverse
Image for post
Image for post
See Control The Scroll.
Image for post
Image for post
See Primary
Image for post
Image for post
See The Physics of Scrolling
Image for post
Image for post
See Shrinkwrap The Scroll
Image for post
Image for post

The Center Is Key

Ok, I lied. There are a couple more parameters unique to the CustomScrollView widget. You caught me! The named parameter, center, goes all the way back to the base class, ScrollView. It’s only used by the CustomScrollView widget. The key passed to this parameter must be the key to one of the other ‘Sliver’ widgets that are passed to the CustomScrollView’s property, slivers.

Note, with the parameter, center, assigned the key for the ‘dragonfly picture’, the second screenshot shows the app example starting up with the third appBar now in ‘centre stage’ (in plain view) on the screen. In other words, the reference widget starts the ‘viewport’ — the visible portion of the scrollable area.

Anchor Your Scrolling

The next named parameter yet covered is called, anchor. It’s the offset — the point the scrolling area begins on the screen on the main axis.

Image for post
Image for post

In the example below, if the anchor is 0.5 and the scrolling is vertical (going up and down), then the zero scroll offset is vertically centered within the viewport — the visible portion of the scrolling area.

Image for post
Image for post
See There’s A Cache

The Semantics of Scrolling

The number of children that will contribute semantic information.

Image for post
Image for post
See It’s A Drag

# Source code as of May 13, 2019
^ Source code as of May 14, 2019

Image for post
Image for post
Decode Flutter Series

→ Other Stories by Greg Perry

Image for post
Image for post
DECODE Flutter on YouTube

Written by

Freelance Developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store