Flutter Interview Prep #7 — Slivers & CustomScrollView: Building Collapsing App Bars and Mixed Scroll Regions


Flutter Interview Prep #7 — Slivers & CustomScrollView

Building Collapsing App Bars and Mixed Scroll Regions

NACHIKETA PANDEY Thursday, July 23, 2026
Flutter Solution Team · Interview Series · 10 min read

You’re building a social profile screen. You need a hero image that collapses into an app bar as you scroll up, followed by a grid of the user's latest photos, followed by a vertical list of their recent activity. And it all needs to scroll flawlessly as one continuous gesture.

If you approach this with a ListView containing a GridView and another ListView, you are going to hit a wall. You'll likely see the dreaded Vertical viewport was given unbounded height error. If you forcefully wrap them in Expanded or give them fixed heights, they will scroll independently—your thumb will get trapped scrolling the grid while the rest of the screen stays locked in place.

To solve this, Flutter doesn't ask you to hack gesture detectors. It asks you to change the underlying layout protocol. Enter Slivers.

Interactive Demo: Scroll down inside the phone frame to see the SliverAppBar collapse.

@JaneDoe
Photos (SliverGrid)
Activity (SliverList)

01 The Problem: When ListView Breaks Down

A standard ListView expects children that obey the Box Protocol. But a box only knows about width and height. When you put a GridView inside a ListView, the parent list asks the grid: "How tall are you?" The grid replies: "I'm scrollable, I can be infinitely tall!" The layout algorithm panics.

To combine multiple scrollable regions into one seamless scroll experience, we need widgets that understand their position relative to a viewport, not just a static bounding box.

02 Box Protocol vs. Sliver Protocol

In Post #5, we learned the Box Layout Protocol: Constraints go down, Sizes go up. Slivers use the exact same philosophical approach, but with a different currency.

Instead of receiving BoxConstraints (min/max width and height), a RenderSliver receives SliverConstraints. This includes vital scrolling context:

  • scrollOffset: How far has the user scrolled past this sliver?
  • overlap: Is a pinned header overlapping this sliver?
  • viewportMainAxisExtent: How big is the visible screen area?

Instead of returning a Size, the sliver returns a SliverGeometry, which tells the parent viewport:

  • scrollExtent: How much total scrolling distance this sliver takes up.
  • paintExtent: How much visual space it currently occupies on screen.
  • cacheExtent: Space consumed just off-screen for layout preparation.

03 CustomScrollView: The Gestural Conductor

A CustomScrollView is the container that makes slivers work. It doesn't paint anything itself. Instead, it takes a list of slivers, provides them with a shared viewport, and tracks a single scroll position.

As the user drags their finger, the CustomScrollView calculates the new scroll offset and passes the updated SliverConstraints down to its children in order. The first sliver consumes what it can. If the scroll offset exceeds the first sliver's size, the remainder is passed to the second sliver, and so on. This is how you get a grid and a list to scroll as one.

04 The Core Sliver Arsenal

SliverList

When to use: You have a linear sequence of variable-height children that should be built lazily as they scroll into view.

SliverGrid

When to use: You need a 2D arrangement of items (like a photo gallery) participating in a larger scroll view.

SliverAppBar

When to use: You want a top header that shrinks, fades, or disappears as the user scrolls down, using pinned, floating, or snap behaviors.

📌

SliverPersistentHeader

When to use: You need a custom element (like a filter bar) that sticks to the top of the screen when scrolled to, beyond what SliverAppBar offers.

05 The ListView Secret

There is a common misconception that ListView.builder is fundamentally different from slivers. Let's peel back the curtain.

Tap to reveal: What is ListView.builder internally?
CustomScrollView + SliverList.builder

When you use ListView.builder, Flutter is quietly wrapping a SliverList inside a CustomScrollView for you. The lazy-building mechanics (instantiating only the children visible in or near the viewport) belong to RenderSliverList. Reaching for CustomScrollView isn't about gaining performance; it's about gaining composability when you need multiple distinct slivers in one view.

06 KeepAlive: State vs. Memory

Because slivers are inherently lazy, children are destroyed when they scroll out of the cacheExtent. If you have a list of playing videos or complex expanded cards, scrolling them off-screen means losing that state.

Flutter solves this with AutomaticKeepAliveClientMixin (or by setting keepAlive: true on specific items). It flags the element to remain in the tree even when painted off-screen.

⚠️ The Memory Trade-off

Every kept-alive item stays fully resident in memory. If you use KeepAlive on an infinite feed of heavy image cards, you defeat the entire purpose of sliver laziness. Your app's memory footprint will grow linearly as the user scrolls, eventually leading to an Out-Of-Memory (OOM) crash. Use it sparingly—only for items actively maintaining critical state.

07 Walkthrough: The Mixed Scroll Screen

Let's build a realistic interview scenario: A collapsing profile header, followed by a grid of photos, followed by a list of recent activity.

Dart
CustomScrollView(
  slivers: [
    // 1. Collapsing Header
    SliverAppBar(
      expandedHeight: 250.0,
      pinned: true, // Sticks to the top when collapsed
      flexibleSpace: FlexibleSpaceBar(
        title: Text('Jane Doe'),
        background: Image.network('profile.jpg', fit: BoxFit.cover),
      ),
    ),
    
    // 2. Grid of Photos
    SliverGrid.builder(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 3,
        mainAxisSpacing: 2.0,
        crossAxisSpacing: 2.0,
      ),
      itemCount: 9,
      itemBuilder: (context, index) => Container(color: Colors.grey),
    ),
    
    // 3. Activity List
    SliverList.builder(
      itemCount: 20,
      itemBuilder: (context, index) {
        return ListTile(
          leading: Icon(Icons.history),
          title: Text('Activity Item #$index'),
        );
      },
    ),
  ],
)

08 Tying it Back to Layout Constraints

As we discussed in Post #5 and Post #6, Flutter’s rendering pipeline avoids multiple layout passes. The sliver protocol is just the box protocol adapted for infinite space. Information flows down as constraints (viewport size, scroll offset), and geometry flows back up (paint extent, scroll extent). Flutter didn't invent a new philosophy for scrolling; it proved that its core constraint-negotiation model is robust enough to handle UI's hardest layout problem.

🧠 Interviewer's Follow-Up

Q: "Why can't you just put a SliverAppBar inside a regular ListView?"
Pointer: ListView expects children that use the Box Protocol. SliverAppBar uses the Sliver Protocol. They speak different layout languages. CustomScrollView acts as the translator/host for slivers.


Q: "How do you add padding around a SliverList?"
Pointer: You cannot use a regular Padding widget (Box Protocol) directly inside the slivers list. You must use SliverPadding, which wraps a sliver and outputs adjusted SliverGeometry.

💬 Senior-Level Sample Answer

"When would you reach for CustomScrollView instead of a plain ListView?"

"I reach for CustomScrollView the moment a screen requires multiple distinct scrolling regions—like a grid followed by a list, or an animating app bar—that need to react to a single unified user gesture. A standard ListView is great for homogeneous data, but because it enforces the box protocol on its children, trying to nest other scrollable widgets inside it leads to gesture conflicts and viewport errors. CustomScrollView solves this by enforcing the sliver protocol, allowing multiple slivers to negotiate how they share the scroll offset and viewport space natively."

✅ Quick Self-Check

Without looking at the code above, can you explain the difference between SliverConstraints and BoxConstraints? What does a parent viewport need to know from a sliver that it doesn't need to know from a standard box?

Official Resources

Written by Flutter Solution · Covering the Flutter & Dart ecosystem from Mumbai, India
fluttersolution.com

Post a Comment

0 Comments