skip to content

In Flutter's SliverAppBar, how do the pinned, floating and snap flags change what the bar does as the user scrolls?

level: middleimportance: should knowfreq 50%

answer

  1. three booleans, all default false
  2. pinned keeps the collapsed toolbar
  3. floating returns on reverse scroll
  4. snap requires floating or asserts
  5. expandedHeight down to collapsedHeight

basics

~20 s

SliverAppBar's pinned keeps the bar on screen collapsed to its toolbar; floating brings it back as soon as the user scrolls toward it, not only at the top; snap, legal only with floating, animates it fully in or out.

solid answer

~40 s

`SliverAppBar` is the `AppBar` as a sliver, so it must sit in a `CustomScrollView`. With `pinned`, `floating` and `snap` all at their default `false`, it scrolls away with the content and comes back only when the list returns to the top. `pinned: true` collapses it from `expandedHeight` to its collapsed height (the toolbar plus any `bottom`) and keeps that on screen. `floating: true` makes it reappear on the first scroll back up, mid-list, tracking the finger. `snap: true` needs `floating: true` — otherwise an assertion fires — and turns that partial reveal into an animation to fully shown or fully hidden. `pinned` and `floating` combine: the toolbar stays while the expanded part floats back. `SliverAppBar.medium` and `.large` default `pinned` to `true`.

code

dart · 20 lines
dart
import 'package:flutter/material.dart';

class HotelPhotoHeader extends StatelessWidget {
  const HotelPhotoHeader({super.key});

  @override
  Widget build(BuildContext context) {
    return SliverAppBar(
      pinned: true,
      floating: true,
      snap: true,
      stretch: true,
      expandedHeight: 260,
      flexibleSpace: FlexibleSpaceBar(
        title: const Text('Harbour View Hotel'),
        background: Image.asset('assets/hotel_cover.jpg', fit: BoxFit.cover),
      ),
    );
  }
}

go deeper

for a junior

Remember the three flags, that they default to false, and that snap needs floating.

for a middle

Explain how expandedHeight, collapsedHeight and bottom decide what collapses, and what each flag combination looks like mid-scroll.

for a senior

Pick the combination from the screen's job, and know the pinned-plus-floating-with-bottom default and the medium and large pinned defaults.

for a principal

Weigh header space against always-visible actions across a product and settle one scroll-header convention per screen type.

## What SliverAppBar is `SliverAppBar` is Material's app bar packaged as a **sliver**, so it only works inside a scroll view that takes slivers, normally a `CustomScrollView`. Built on a persistent-header render object, it can **shrink** from an expanded height to a collapsed height as the user scrolls, and its `flexibleSpace` (usually a `FlexibleSpaceBar`) fills the expanded area with a photo, a title that scales, or both. Three booleans decide how it behaves as content moves under it. ## The three flags All three default to `false` on the plain `SliverAppBar` constructor. | `pinned` | `floating` | `snap` | Behaviour | |---|---|---|---| | false | false | false | Scrolls away with the content; reappears only when the list is back at the top. | | false | true | false | Reappears as soon as the user scrolls toward it, mid-list, following the finger. | | false | true | true | Same trigger, but it animates to fully shown or fully hidden instead of stopping half way. | | true | false | false | Shrinks to its collapsed height and stays; the expanded part returns only at the top. | | true | true | false | The collapsed part stays; the expanded part floats back on any upward scroll. | | true | true | true | As above, with the snap animation. | | any | false | true | Assertion: `The "snap" argument only makes sense for floating app bars.` | The docs add one more effect of `snap`: when something inside a floating bar asks to be shown on screen — a `TextField` gaining focus, for example — a snapping bar expands fully to reveal it. ## Heights that decide the collapse - **`expandedHeight`** — the height before any scrolling. It defaults to the toolbar plus the `bottom` widget, so without it there is nothing to collapse. - **`collapsedHeight`** — by default `toolbarHeight` (`kToolbarHeight`, 56 logical pixels) plus the `bottom` widget's preferred height, plus the top safe-area padding when `primary` is true. - **`bottom`** — a `PreferredSizeWidget`, usually a tab row, that stays part of the collapsed bar when pinned. - A subtle case from the source: with `pinned` **and** `floating` true and a `bottom` set, the default collapsed height is **only** the `bottom` plus the top padding. The toolbar floats away and the tab row stays — a pattern many apps want. ## FlexibleSpaceBar and stretch `FlexibleSpaceBar` lays out the expanded area. Its `collapseMode` defaults to `CollapseMode.parallax`, so a `background` image scrolls slower than the content as it collapses; `pin` fixes it in place and `none` scrolls it normally. `SliverAppBar.stretch` (default `false`) lets the bar grow into the overscroll area on physics that allow overscroll, such as the iOS-style bounce; `stretchModes` on the flexible space defaults to zooming the background, and `onStretchTrigger` fires once overscroll passes `stretchTriggerOffset`, which defaults to 100.0. ## Material 3 medium and large bars `SliverAppBar.medium` and `SliverAppBar.large` build the Material 3 medium and large top app bars: a large title in the expanded area that collapses into the toolbar. Unlike the plain constructor they default to **`pinned: true`**, which surprises people who copy flags between them. ## Choosing a combination 1. Long reading screen where the header is decoration: all `false`, so the space goes back to content. 2. Feed where users need the bar's actions often: `floating: true`, with `snap: true` if half-shown bars look broken. 3. Detail page with a hero photo and actions that must stay reachable: `pinned: true` with an `expandedHeight` and a `FlexibleSpaceBar`. 4. Tabs that must stay while the title can go: `pinned` and `floating` with a `bottom` tab row.

  • What changes with SliverAppBar.medium and SliverAppBar.large?
    They build the Material 3 medium and large top app bars: the title starts large in the expanded area and shrinks into the toolbar as you scroll. Both default `pinned` to `true`, unlike the plain `SliverAppBar` constructor, where every mode flag defaults to `false`.
  • With pinned and floating both true and a TabBar as bottom, what stays on screen?
    By default the collapsed height is only the `bottom` widget's preferred height plus the top padding. So the toolbar with the title scrolls away, the tab row stays pinned, and an upward scroll floats the toolbar back in.
  • Why does a SliverAppBar not work as Scaffold.appBar?
    `Scaffold.appBar` expects a box widget with a preferred size, while `SliverAppBar` produces a sliver that speaks the scroll-offset protocol. It has to be a child of a `CustomScrollView` (or the header of a `NestedScrollView`); the plain `AppBar` is the box version.

saying these in an interview costs you the question

  • snap works on its own without setting floating.
  • pinned: true keeps the bar at its full expanded height.
  • floating means the bar overlays content like a floating button.
  • SliverAppBar can be passed straight to Scaffold.appBar.
  • The plain SliverAppBar constructor is pinned by default.