Mastering Flutter Slivers for Custom Scroll Effects

Mastering Flutter Slivers for Custom Scroll Effects

What You’ll Learn

Slivers are the building blocks of scrollable areas in Flutter. Understanding them unlocks powerful custom scroll effects like collapsing headers, parallax effects, and mixed-content scrolling that would be complex to achieve otherwise.

Example

Here’s a practical example with a collapsing app bar and a grid of items:

import 'package:flutter/material.dart';

class SliverDemoScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          // Collapsing header
          SliverAppBar(
            expandedHeight: 200,
            floating: false,
            pinned: true,
            flexibleSpace: FlexibleSpaceBar(
              title: Text('My Gallery'),
              background: Image.network(
                'https://picsum.photos/800/400',
                fit: BoxFit.cover,
              ),
            ),
          ),
          
          // Sticky header
          SliverPersistentHeader(
            pinned: true,
            delegate: _StickyHeaderDelegate(
              child: Container(
                color: Colors.blue,
                padding: EdgeInsets.all(16),
                child: Text('Photos', style: TextStyle(color: Colors.white)),
              ),
            ),
          ),
          
          // Grid of items
          SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              mainAxisSpacing: 8,
              crossAxisSpacing: 8,
            ),
            delegate: SliverChildBuilderDelegate(
              (context, index) => Card(
                child: Center(child: Text('Item $index')),
              ),
              childCount: 20,
            ),
          ),
        ],
      ),
    );
  }
}

class _StickyHeaderDelegate extends SliverPersistentHeaderDelegate {
  final Widget child;
  
  _StickyHeaderDelegate({required this.child});
  
  @override
  Widget build(context, shrinkOffset, overlapsContent) => child;
  
  @override
  double get maxExtent => 50;
  
  @override
  double get minExtent => 50;
  
  @override
  bool shouldRebuild(covariant oldDelegate) => false;
}

Key concepts:

Try It Yourself

Modify the example to add a SliverToBoxAdapter between the header and grid that displays a horizontal scrolling list of categories. This teaches you how to mix different sliver types and embed non-sliver widgets.

SliverToBoxAdapter(
  child: Container(
    height: 60,
    child: ListView.builder(
      scrollDirection: Axis.horizontal,
      itemCount: 10,
      itemBuilder: (context, index) => Chip(label: Text('Category $index')),
    ),
  ),
),

Tip of the Day

Use SliverFillRemaining when you need a widget to fill the remaining viewport space—perfect for empty states or loading indicators that should center in available space regardless of other content.