← Back to posts Cover image for Mastering Advanced Scrolling UI with Flutter Slivers: Beyond Basic Lists

Mastering Advanced Scrolling UI with Flutter Slivers: Beyond Basic Lists

· 5 min read
Weekly Digest

The Flutter news you actually need

No spam, ever. Unsubscribe in one click.

Chris
By Chris

Let’s be honest — we’ve all been there. You start with a simple ListView or SingleChildScrollView, but then the design mockup arrives. It shows a header that sticks, a hero section that shrinks as you scroll, and multiple lists that collapse into each other. Suddenly, a basic ListView feels like trying to build a skyscraper with Lego Duplo.

This is where Flutter’s Sliver widgets come in. They are your framework for building custom, performant, and truly dynamic scrollable layouts. While they have a reputation for being complex, mastering them unlocks a whole new tier of UI polish.

What Are Slivers, Really?

Think of a normal scrollable widget (ListView, GridView) as a monolith: it decides how all its children are arranged and scrolled. Slivers break that monolith apart. A Sliver is simply a slice of a scrollable area. Each Sliver widget manages its own little piece of the scroll view’s geometry and behavior.

You compose these slices inside a CustomScrollView using its slivers property. This is the key: you can mix and match different Sliver behaviors in one smooth, coordinated scroll.

The Foundation: CustomScrollView and Common Slivers

Here’s the basic setup you’ll always start with:

CustomScrollView(
  slivers: <Widget>[
    // Your slivers go here, in order.
  ],
)

Common building block Slivers include:

  • SliverAppBar: The star of the show for collapsing headers.
  • SliverList: A list of children, just like ListView, but for sliver land.
  • SliverGrid: A grid, like GridView.
  • SliverToBoxAdapter: A wrapper that lets you put any regular Widget (like a Container or Padding) into the sliver list.
  • SliverPadding & SliverSafeArea: Add padding and safe areas specifically to your sliver layout.

Building Dynamic Layouts: Practical Examples

1. The Collapsing & Sticky App Bar

This is the classic use case. The SliverAppBar can expand, collapse, pin itself, and even manage overlapping content.

CustomScrollView(
  slivers: [
    SliverAppBar(
      expandedHeight: 200.0,
      pinned: true, // Stays visible when collapsed
      floating: false,
      snap: false,
      flexibleSpace: FlexibleSpaceBar(
        title: const Text('Dynamic News Feed'),
        background: Image.network(
          'https://picsum.photos/id/1/800/600',
          fit: BoxFit.cover,
        ),
      ),
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (context, index) => ListTile(title: Text('News Item $index')),
        childCount: 50,
      ),
    ),
  ],
);

The pinned: true is what makes the app bar’s toolbar stick to the top after collapsing. expandedHeight defines the full size before scroll.

2. Mixing Lists, Grids, and Static Widgets

Need a profile header, followed by a grid of photos, then a list of comments? Slivers make this trivial.

CustomScrollView(
  slivers: [
    // Static Profile Header
    SliverToBoxAdapter(
      child: UserProfileHeader(),
    ),
    // Section Title (also static)
    SliverToBoxAdapter(
      child: Padding(
        padding: EdgeInsets.all(16.0),
        child: Text('Photo Gallery', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
      ),
    ),
    // A grid of photos
    SliverGrid(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 3,
        mainAxisSpacing: 4.0,
        crossAxisSpacing: 4.0,
      ),
      delegate: SliverChildBuilderDelegate(
        (context, index) => Image.network('https://picsum.photos/id/$index/200/200'),
        childCount: 12,
      ),
    ),
    // Another section title
    SliverToBoxAdapter(
      child: Padding(
        padding: EdgeInsets.all(16.0),
        child: Text('Recent Activity', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
      ),
    ),
    // The list of activity items
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (context, index) => ActivityListItem(index: index),
        childCount: 30,
      ),
    ),
  ],
);

3. Creating Sticky Headers Between Lists

You can pin section headers as you scroll, not just the main app bar, using SliverPersistentHeader. This is perfect for categorized lists.

CustomScrollView(
  slivers: [
    SliverAppBar(...), // Your main app bar
    _buildStickyHeader('Fruits'),
    _buildSliverListForCategory('fruits'),
    _buildStickyHeader('Vegetables'),
    _buildSliverListForCategory('vegetables'),
  ],
);

// Helper method to create a pinned header
Widget _buildStickyHeader(String title) {
  return SliverPersistentHeader(
    pinned: true,
    delegate: _StickyHeaderDelegate(title: title),
  );
}

class _StickyHeaderDelegate extends SliverPersistentHeaderDelegate {
  _StickyHeaderDelegate({required this.title});
  final String title;

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(
      color: Colors.white,
      child: Padding(
        padding: EdgeInsets.all(16.0),
        child: Text(title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
      ),
    );
  }

  @override
  double get maxExtent => 50.0; // Height when fully expanded
  @override
  double get minExtent => 50.0; // Height when fully collapsed

  @override
  bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }
}

Common Pitfalls and Tips

  1. SliverToBoxAdapter is Your Friend: You can’t just put a Container directly in the slivers list. Wrap any non-sliver widget with SliverToBoxAdapter.
  2. Mind the Performance: Use SliverChildBuilderDelegate for lists and grids with many children. It lazily creates children as they scroll into view, just like ListView.builder.
  3. The Order Matters: Slivers are painted in the order they appear in the slivers list. Your layout is a vertical (or horizontal) stack of these sliver sections.
  4. Debugging: If your layout looks wrong, check that you’re using Sliver variants of common widgets (SliverPadding, not Padding directly inside a sliver).

Taking It Further

Once you’re comfortable with the basics, explore SliverAnimatedOpacity, SliverFadeTransition, and SliverStaggeredGrid (from the flutter_staggered_grid_view package) for even more advanced effects. You can also create custom SliverPersistentHeaderDelegate objects to build headers with complex scroll-driven animations.

The learning curve is worth it. Moving from fighting against simple scroll views to composing precise, performant scroll layouts with slivers is a game-changer. It transforms your Flutter apps from functional to fluid and professional. Start by converting one complex screen in your app, and you’ll quickly see the power it puts in your hands.

This blog is produced with the assistance of AI by a human editor. Learn more

Related Posts

Cover image for Flutter State Management: Choosing the Right Solution for Your Project

Flutter State Management: Choosing the Right Solution for Your Project

Flutter developers often struggle with selecting the optimal state management solution given the myriad of options. This post will provide a practical framework for evaluating popular choices like Riverpod, Bloc, Provider, and GetX, discussing their strengths, weaknesses, and ideal use cases to help teams make informed decisions based on project complexity and team experience.

Cover image for Mastering Advanced Scrolling UI with Flutter Slivers: Beyond Basic Lists

Mastering Advanced Scrolling UI with Flutter Slivers: Beyond Basic Lists

Flutter's Sliver widgets offer powerful capabilities for creating highly customized and performant scrollable UIs, but they are often underutilized or misunderstood. This post will demystify Slivers, providing practical examples for implementing complex scroll effects like sticky headers, expanding app bars, and sections that shrink or collapse, guiding developers to build truly dynamic user interfaces.

Cover image for Mastering In-App Subscriptions in Flutter: RevenueCat vs. `in_app_purchase` for Scalability

Mastering In-App Subscriptions in Flutter: RevenueCat vs. `in_app_purchase` for Scalability

Flutter developers frequently struggle with implementing in-app purchases and subscriptions, often debating between the complexity of the native `in_app_purchase` package and the convenience of third-party solutions like RevenueCat. This post will provide a practical comparison, offering insights into implementation, monitoring, and compliance considerations to help developers choose the right strategy for their app's long-term billing needs.