Shubham Soni Product Engineer @ threedots | Google India Scholar 2018 | Technical Author @ LogRocket

Building a custom Flutter ScrollView

4 min read 1199

Building a Custom Flutter ScrollView

In this article, we’ll learn how to use CustomScrollView and Slivers to create a custom scrolling screen with multiple scrollable components scrolling horizontally, vertically, or simply putting a non-scrollable widget inside CustomScrollView.

This widget is especially useful if you want to create custom scrolling effects or have more control over ScrollView’s scrolling content.

If you build a custom ScrollView like mine, you should end up with this result:

Flutter Custom ScrollView Final Demo

Table of contents

What exactly are Slivers?

A sliver is a portion of a scrollable area that you can configure to behave differently.

As the official docs state, a sliver is a small portion of a scrollable area inside a CustomScrollview that can be configured accordingly to behave in a certain way. Using Flutter’s Slivers, we can easily create a plethora of fantastic scrolling effects. Slivers are used by all scrollable views in Flutter; for example, ListView uses SliverList and GridView uses SliverGrid.

Because Slivers lazily build their views when the widgets come into the viewport, it is really useful to show a great number of children without worrying about memory issues. Moreover, we can easily make lots of fantastic scrolling effects by using Flutter Slivers.

Understanding CustomScrollView

CustomScrollView is a widget that uses multiple Slivers rather than just one, as we saw with ListView and GridView. It enables you to directly utilize Slivers to create scrolling effects such as lists, grids, and expanding headers.

The implementation is straightforward; simply place all of the Slivers inside the CustomScrollView as shown:

CustomScrollView(
    slivers: [
      SliverAppBar(...),
    SliverToBoxAdapter(...),
      SliverGrid(...),
        SliverToBoxAdapter(...),
    SliverFixedExtenrList(...),
    ],
    controller: ScrollController(),
)

Give CustomScrollView a ScrollController to control the initial scroll offset or to handle scroll behavior in general.

Introduction to Slivers

As previously stated, a Sliver is a portion of a scrollable area that can be used inside a CustomScrollView. There are numerous Slivers available, and we’ll select a few to demonstrate how to use them later.

  • SliverAppBar: This sliver renders an app bar, and it is one of the most commonly used sliver widgets that creates collapsible AppBars by setting both the flexibleSpace and expandedHeight parameters
  • SliverList: A sliver that renders a list in a linear array along the ScrollView’s main axis. To build list items as they scroll into view, SliverList accepts a delegate parameter. A SliverChildListDelegate specifies the fixed list of children that are created all at once, whereas a SliverChildBuilderDelegate specifies how they are built lazily
  • SliverFixedExtentList: SliverFixedExtentList is identical to SliverList, with the exception that SliverFixedExtentList guarantees that all list items will have the same size on the main axis (i.e., equal height on a vertical scrolling list or equal width on a horizontal scrolling list). This has a significant impact on the performance of the ListView when scrolling because knowing the size of the items before loading them is quite beneficial when you wish to jump a long distance. For example, if we know each item is fixed at 50px tall and we want to scroll 5,000px down, we can easily leap 100 things by loading the 101st item and displaying it
  • SliverGrid: SliverGrid is a sliver that displays a 2D array of children in a ScrollView. It accepts children via a delegate or an explicit list, and it also defines gridDelegate, which determines the location of the children widget within the grid
  • SliverPadding: A sliver that creates empty space around another sliver. The only difference between it and the Padding widget is that it generates RenderSliver rather than RenderBox, so that it can be used inside CustomScrollView
  • SliverToBoxAdapter: A sliver that allows you to warp any other widget that isn’t a Sliver and use it inside CustomScrollView; this is a very useful sliver for creating intricate scrolling screens with various widgets
  • SliverOpacity: A sliver widget that makes its sliver child partially transparent. It is an easy-to-use replacement to the Opacity widget, just like SliverPadding

Building the custom Flutter ScrollView

Enough talking; now it’s time to dive into the code and apply all we’ve learned about CustomScrollView and Slivers. We’ll build an example app with multiple scrollable/non-scrollable widgets aligned vertically in a scroll view that functions as a single scrollable area.

Here’s the structure of our CustomScrollView:

  • SliverAppBar: A collapsible AppBar that expands and collapses as we scroll vertically. You can experiment with the pinned, floating, and snap parameters to control how and when the SliverAppBar collapses, expands, or becomes pinned to the top as we scroll further
  • SliverToBoxAdapter: We’ll place a Container with height 200 and color tealAccent below the SliverAppBar to represent a non-Sliver widget that needs to be wrapped with SliverToBoxAdapter in order to be children of CustomScrollView
  • SliverGrid: Underneath the Container is a SliverGrid with twenty child items each with a different color flavor of tealAccent. We used SliverGridDelegateWithMaxCrossAxisExtent, which selects the largest cross-axis extent for the tiles; if you want to create a layout with a fixed number of tiles in the cross axis, use SliverGridDelegateWithFixedCrossAxisCount
  • SliverToBoxAdapter: Below SliverGrid, we have a Container widget again with amberAccent color
  • ListView (Inside SliverToBoxAdapter): You might just be wondering why we used a ListView inside a SliverToBoxAdapter. The reason is that we wanted to make a list that scrolls horizontally, which is not possible with SliverList, and since we had to place this `ListView` inside CustomScrollView, we need to add this inside SliverToBoxAdapter
  • SliverFixedExtentList: As the final sliver child, we’re using SliverFixedExtentList to generate a vertically scrolling list with a fixed itemExtent, which means that each child will have a fixed height, which will help us improve scrolling performance:
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: _title,
      home: CustomScrollingWidget(),
    );
  }
}

class CustomScrollingWidget extends StatefulWidget {
  const CustomScrollingWidget({Key? key}) : super(key: key);

  @override
  State createState() => _CustomScrollingWidgetState();
}

class _CustomScrollingWidgetState extends State {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          const SliverAppBar(
            pinned: true,
            expandedHeight: 250.0,
            flexibleSpace: FlexibleSpaceBar(
              title: Text('CustomScrollView'),
              centerTitle: true,
            ),
          ),
          SliverToBoxAdapter(
            child: Container(
              color: Colors.tealAccent,
              alignment: Alignment.center,
              height: 200,
              child: const Text('This is Container'),
            ),
          ),
          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.teal[100 * (index % 9)],
                  child: Text('Grid Item $index'),
                );
              },
              childCount: 20,
            ),
          ),
          SliverToBoxAdapter(
            child: Container(
              color: Colors.amberAccent,
              alignment: Alignment.center,
              height: 200,
              child: const Text('This is Container'),
            ),
          ),
          SliverToBoxAdapter(
            child: SizedBox(
              height: 100.0,
              child: ListView.builder(
                scrollDirection: Axis.horizontal,
                itemCount: 10,
                itemBuilder: (context, index) {
                  return SizedBox(
                    width: 100.0,
                    child: Card(
                      color: Colors.cyan[100 * (index % 9)],
                      child: Text('Item $index'),
                    ),
                  );
                },
              ),
            ),
          ),
          SliverFixedExtentList(
            itemExtent: 50.0,
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return Container(
                  alignment: Alignment.center,
                  color: Colors.lightBlue[100 * (index % 9)],
                  child: Text('List Item $index'),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

Conclusion

In this article, you learned how to make a ScrollView with numerous scrolling components and now you can design any type of complicated scrollable screen using Slivers and CustomScrollView in this article. I hope you continue to explore new things.

Now that we’ve got everything ready, all you have to do is start the application and enjoy.



Best wishes! Have fun fluttering!

If you have any questions, please post them here. Any and all feedback is appreciated.

Get setup with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.
  3. $ npm i --save logrocket 

    // Code:

    import LogRocket from 'logrocket';
    LogRocket.init('app/id');
    Add to your HTML:

    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • ngrx middleware
    • Vuex plugin
Get started now
Shubham Soni Product Engineer @ threedots | Google India Scholar 2018 | Technical Author @ LogRocket

Leave a Reply