Deterministic Back Behavior

Define a single back policy so overlay dismissal and stack pops always happen in the same order. Decide the next action from the current overlay, stack, and tab state.

Overlay-first

Dismiss active overlays before popping page stacks. Only allow a page pop after no active overlay remains.

Root-aware

When stack length is one, delegate to parent behavior instead of force-pop. This preserves the parent or app-level back policy.

Tab-aware

In tab mode, move to the first tab at root before triggering app-level exit handling. Trigger exit handling only after the tab root has been reached.

Back Scope Pattern

Use DeclarativePagesBackScope for page stacks and tab callbacks for tab roots. Keep overlay dismissal, page popping, and root handling in that priority order.

Back Policy

DeclarativePagesBackScope

return DeclarativePagesBackScope(
  pages: _pages,
  popTopPage: _popTop,
  isOverlayVisible: _overlay != null,
  dismissOverlay: _dismissOverlay,
  onBackAtRoot: _onBackAtRoot,
  child: DeclarativePagesNavigator(
    pages: _pages,
    buildPage: _buildPage,
    onPopTop: _popTop,
    canPopTop: () => _overlay == null,
  ),
);
UX Stability

Keep one consistent order for all back events across every platform. This avoids confusion between Android system back and iOS gestures.