build method
Builds the widget subtree for this state.
Implementation
@override
Widget build(BuildContext context) {
// Collect all overlay entries from all routes.
final entries = <OverlayEntry>[];
for (final route in _routes) {
entries.addAll(route.overlayEntries);
}
if (entries.isEmpty) {
return SizedBox.shrink();
}
// Find the lowest opaque entry — everything from there upward is visible.
var opaqueIndex = 0;
for (var i = entries.length - 1; i >= 0; i--) {
if (entries[i].opaque) {
opaqueIndex = i;
break;
}
}
// Build children list using _RouteEntry wrappers with stable keys.
// Offstage entries come first and active entries come last.
// Element.dispatch walks children in reverse order for KeyMsg (one-winner
// policy), so placing active entries last ensures they receive keyboard
// input before offstage entries.
// Offstage entries render as zero-size (SizedBox 0x0) and their
// handleIntercept returns Cmd.none() to suppress message delivery to
// their children.
//
// Using a single _RouteEntry type with a stable Key for each entry
// ensures Widget.canUpdate returns true when an entry transitions
// between active↔offstage (same runtimeType + same Key). The
// _RouteEntry.build() always returns SizedBox(child: widget.child)
// keeping the subtree structure identical and preserving child
// State objects and pending Futures.
final children = <Widget>[];
// Offstage entries first (below opaqueIndex, maintainState only).
for (var i = 0; i < opaqueIndex; i++) {
if (entries[i].maintainState) {
final key = _entryKeys[entries[i]];
children.add(
_RouteEntry(
key: key,
offstage: true,
child: entries[i].builder(context),
),
);
}
}
// Active entries last (from opaqueIndex onward).
for (var i = opaqueIndex; i < entries.length; i++) {
final key = _entryKeys[entries[i]];
children.add(_RouteEntry(key: key, child: entries[i].builder(context)));
}
// Always use a Stack — even for a single child — so the element tree
// structure remains stable across push/pop cycles and State objects
// are preserved rather than disposed and re-created.
final result = Stack(fit: StackFit.expand, children: children);
// Wrap in FocusScope to trap focus in the navigator.
return FocusScope(isTrapped: true, child: result);
}