Slivers and custom scroll effects

Flutter · Advanced · 14 min

Collapsing headers, mixed lists and grids, and sticky sections all require slivers.

A sliver is a piece of a scrollable area that lays itself out lazily according to the scroll position. ListView and GridView are convenience wrappers around a single sliver. To mix several kinds of content in one scroll, use a `CustomScrollView` with a list of slivers:

  • `SliverAppBar` — an app bar that can expand, collapse (expandedHeight, flexibleSpace: FlexibleSpaceBar(...)), stay pinned, or reappear when scrolling up (floating, snap).
  • `SliverList` with SliverChildBuilderDelegate, or the convenient SliverList.builder / SliverList.separated — lazy lists.
  • `SliverGrid` — lazy grids.
  • `SliverToBoxAdapter` — wraps one ordinary (box) widget.
  • `SliverPadding`, `SliverFillRemaining`, `SliverPersistentHeader` (custom sticky headers), and PinnedHeaderSliver / SliverResizingHeader in recent Flutter versions.

Only sliver widgets may be direct children of slivers:; putting a plain widget there throws. Box widgets go inside SliverToBoxAdapter, and large lists must stay as SliverList to remain lazy — never a Column in an adapter.

Slivers use `SliverConstraints` (scroll offset, remaining paint extent) instead of box constraints, and report a SliverGeometry. That's what lets them shrink, pin or disappear as you scroll.

NestedScrollView coordinates an outer header with inner scrollables, for example tabs under a collapsing app bar.

Example

import 'package:flutter/material.dart';
class ArticleScreen extends StatelessWidget {
  const ArticleScreen({super.key});

  @override
  Widget build(BuildContext context) => Scaffold(
        body: CustomScrollView(slivers: [
          const SliverAppBar(
            pinned: true,
            expandedHeight: 200,
            flexibleSpace: FlexibleSpaceBar(title: Text('Flutter Weekly')),
          ),
          const SliverToBoxAdapter(
            child: Padding(padding: EdgeInsets.all(16), child: Text('Top picks')),
          ),
          SliverGrid.count(crossAxisCount: 3, children: [
            for (var i = 0; i < 6; i++) Card(child: Center(child: Text('Pick $i'))),
          ]),
          SliverList.builder(                       // lazy, any length
            itemCount: 50,
            itemBuilder: (context, i) => ListTile(title: Text('Story #$i')),
          ),
        ]),
      );
}

Remember

  • CustomScrollView combines several slivers in one scroll
  • SliverAppBar gives collapsing, pinned and floating headers
  • Only slivers go in slivers:; wrap box widgets in SliverToBoxAdapter

Watch out for

  • Putting a big Column of items in a SliverToBoxAdapter, losing lazy building.

Check yourself

  1. You need a normal Text widget between a SliverAppBar and a SliverList. How do you add it?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: api.flutter.dev/flutter/widgets/CustomScrollView-class.html

Builds on

Unlocks

Nothing builds on this yet.

Slivers and custom scroll effects · Flutter · Topolearn