Skip to content

The app header the shell doesn't have: one slot above four branches, or four copies #226

Description

@maximsan

Part of #6

Question

The design has one app header; the Flutter shell has none. Where does it live, and what happens to it when a branch pushes?

Surfaced resolving Saved shelf, which needs somewhere to put SavedHeaderButton and found no header architecture to put it in.

What the source specifies — measured, do not re-derive

AppHeader (prototype/screens.jsx:671) is rendered once, at app level (prototype/app.jsx:1668), a sibling of the TabBar rather than anything inside a tab:

{showTabs && window.AppHeader && (
  <AppHeader tab={tab} variant={...} scrolled={...} savedCount={savedCount} ... />
)}

It carries:

  • StickyHeaderChrome at height={116}, with HeaderCompactTitle collapsing an eyebrow/title pair on scroll
  • Per-tab copy from APP_HEADER_TITLES[tab] — the header knows which tab it is
  • Right-side entries: SavedHeaderButton (with count badge), DictHeaderButton, and DuelHeaderButton when showDuel
  • A profile variant that replaces all three with a single Settings gear
  • showTabs gating it away on sub-screens, exactly as it gates the TabBar

What the app has instead

AppShell (lib/app/app_shell.dart) is a bare Scaffoldbody: navigationShell, bottomNavigationBar: NavigationBar, and no appBar. Each branch builds its own:

Branch Header today
Learn AppBar(title: Text(AppLabels.tabLearn))
Path AppBar(title: Text(AppLabels.tabPath))
Cards AppBar(title: Text(AppLabels.tabCards))
Profile SliverPersistentHeader + ProfileHeaderDelegate

Three stock Material AppBars and one real one. None of them is the design's header.

Decide

  • Where the slot goes. Above the branch navigators in AppShell — one instance, tab-aware, matching the prototype — or inside each branch, four instances kept in sync. The prototype's shape argues for the shell, but StatefulShellRoute gives each branch its own Navigator, so a shell-level header persists across pushes within a branch unless something hides it.
  • How it hides on sub-screens. The prototype uses one showTabs flag for both header and TabBar. The Flutter equivalent has to be decided: a route-level flag the shell reads, nested navigation that escapes the shell, or per-route AppBar suppression.
  • Where the collapsing scroll state lives. The header collapses on the active tab's scroll. With four independent branch navigators, that means either the shell observes the active branch's scroll or each branch reports upward.
  • What happens to ProfileHeaderDelegate. Profile is the one branch with a real header today and the one the design gives a different variant. Does it fold into the shared header's profile variant, or stay separate?

Not in scope

The scroll / BackdropFilter behaviour of StickyHeaderChrome itself, which stays fog on the map — this ticket decides the slot and its lifecycle, not the chrome's paint.

Resolved when the header's owner, its hide rule, its scroll source and Profile's treatment are all named.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions