Skip to content

Nav3: Multistack support #5650

Description

@0xadam-brown

Problem Statement

Apps with bottom tabs, drawer navigation, or similar patterns often maintain multiple independent backstacks – one per top-level destination – so that users can switch tabs without losing their place. Nav3 doesn't provide a built-in multi-back-stack API; instead, apps own multiple NavBackStack instances and merge their entries before passing them to NavDisplay (see the multiplestacks recipe).

The Stage 1 integration (#5000) and multipane support (#5649) handle a single SnapshotStateList<T> backstack. When an app uses multiple stacks, the SDK needs to understand which stack is active and scope its telemetry accordingly.

Solution Brainstorm

  • Stack identification. Introduce a stack_id concept so breadcrumbs, transactions, and crash context can group navigations per stack. This could be a user-supplied label per stack (e.g. the tab route key's name), or inferred from the NavBackStack identity.
  • API surface. Provide a way to register multiple backstacks with a shared SentryNavStateHolder, or create per-stack holders that coordinate. The app tells us which stack is currently active (likely by passing the active topLevelRoute or equivalent).
  • scope.screen – set to the active stack's top route. When the user switches tabs, this triggers a navigation breadcrumb and updates scope.screen to the new stack's current top.
  • Breadcrumbs – capture both intra-stack navigation (push/pop within a tab) and inter-stack switches (tab change). Include stack_id in breadcrumb data so the two can be distinguished. A tab switch breadcrumb might look like { from: "/settings/profile", to: "/home/feed", stack_id: "home" }.
  • Transactions – when the user switches to a stack whose top entry hasn't changed, should we start a new transaction? Probably not – the screen was already loaded. Only start a transaction on an actual new destination within the stack.
  • Crash context (contexts.navigation) – include all stacks (not just the active one), capped by maxBackstackSize total across stacks. Mark which stack is active.

Open questions:

  • Should contexts.navigation.backstack flatten all stacks into one list, or nest them by stack_id?
  • How do multistack and multipane compose? A tablet app might have bottom tabs (multistack) where one tab shows a list-detail layout (multipane). The primary pane logic from Stage 2 should still apply within the active stack.
  • Should tab switches reuse the existing "navigation" breadcrumb category or get a distinct one (e.g. "stack_switch")?
  • Do we want to revisit Nav2 simplifications alongside this (nested nav graphs, dialog/bottom-sheet transaction behavior)?

Existing Nav2 support

Nav2 supports multiple backstacks natively via NavController.navigate() with saveState = true / restoreState = true, commonly used with NavigationBar or BottomNavigation. However, the current Nav2 integration does not distinguish between stacks – it sees a flat stream of destination changes on a single NavController and treats every destination change identically. There is no stack_id, no differentiation between intra-tab navigation and tab switches, and no per-stack grouping in crash context. A tab switch looks the same as a forward navigation in telemetry.

Metadata

Metadata

Assignees

No one assigned
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions