Layout & Navigation
CDX UI provides four layout / navigation primitives. They cover the common shells without forcing you into a full IDE-style workspace (use cdx_panes for that).
MasterDetailScaffold
Responsive master-detail with an animated, resizable detail panel. Picks docked vs. overlay presentation based on viewport width — or forces one explicitly.
MasterDetailScaffold(
main: AreasList(areas: areas, onSelect: store.select),
panel: AreaDetailPanel(area: store.selected),
showPanel: store.selected != null,
panelWidth: 600,
presentation: const PanelPresentation.auto(breakpoint: 1024),
onPanelClosed: () => store.clearSelection(),
)PanelPresentation
PanelPresentation.docked() // side-by-side row
PanelPresentation.overlay(showBarrier: true) // floating sheet from right
PanelPresentation.auto(breakpoint: 1024) // docked at wide, overlay at narrow| Behavior | Docked | Overlay |
|---|---|---|
| Layout | Row(main, panel) | Stack(main, scrim, panel) |
| Resizable edge | yes (drag handle on left of panel) | no |
| Barrier dismiss | n/a | tap scrim closes (when showBarrier: true) |
| ESC key | both — closes panel via onPanelClosed | both |
Panel sizing defaults are baked in (min 360, default 480, max 720); pass panelWidth to override the initial width.
LazyIndexedStack
IndexedStack that only builds children when first selected. The unbuilt children render SizedBox.shrink(). Once built, children are preserved across switches via Offstage + TickerMode (so background animations pause but state survives).
LazyIndexedStack(
index: tabIndex,
children: const [
OverviewTab(),
DetailsTab(),
HistoryTab(), // only built when the user opens this tab
],
)LazyIndexedStackBuilder is the variant that takes a builder function and an itemCount — useful for very large fixed-tab sets.
RouteAwareTabController + RouteAwareLazyIndexedStack
Tab controllers wired to go_router. Switching tabs updates the URL; URL changes update the active tab. Combined with LazyIndexedStack they give you router-driven tabbed pages without manual sync code.
RouteAwareTabBarView(
tabRoutes: const ['overview', 'details', 'history'],
baseRoute: '/lots/$lotId',
tabs: const [
Tab(text: 'Overview'),
Tab(text: 'Details'),
Tab(text: 'History'),
],
children: const [
OverviewTab(),
DetailsTab(),
HistoryTab(),
],
)Internally this composes:
RouteAwareTabController(tabRoutes, baseRoute, length, vsync)— bidirectional syncRouteAwareTabBar—TabBarwith the controller wiredRouteAwareLazyIndexedStack— lazy-loaded panes
Use the parts directly when you need a custom tab strip.
Wizard
A vertical-sidebar wizard. WizardSteps pair a label with a content builder; WizardController manages navigation, busy state, and sequential gating.
final controller = WizardController(
stepCount: 3,
isSequential: true, // disallow forward jumps to unvisited steps
onStepChanged: (i) => analytics.log('wizard_step', {'step': i}),
);
Wizard(
controller: controller,
steps: [
WizardStep(title: 'Basics', builder: (_) => const _BasicsStep()),
WizardStep(title: 'Details', builder: (_) => const _DetailsStep()),
WizardStep(title: 'Review', builder: (_) => const _ReviewStep()),
],
decoration: BoxDecoration(color: Theme.of(context).colorScheme.surfaceContainerLow),
)WizardController
controller.currentStep; // 0-based
controller.highestStepVisited;
controller.isFirstStep / isLastStep;
controller.canGoToNextStep / canGoToPreviousStep;
controller.canGoToStep(2); // honors isSequential + stepGuard
controller.nextStep();
controller.previousStep();
controller.goToStep(2);
// Async work — block all navigation while busy
controller.setBusy(true);
await api.save();
controller.setBusy(false);
controller.nextStep();Custom step indicators
Pass stepIndicatorBuilder and stepLabelBuilder to override the default themed circles + labels. Useful on dark / gradient backgrounds.
Wizard(
controller: controller,
steps: steps,
decoration: const BoxDecoration(gradient: myGradient),
stepIndicatorBuilder: (context, index, state, size) => MyCustomDot(state: state, size: size),
stepLabelBuilder: (context, title, state, isTappable) => MyLabel(title: title, state: state),
)WizardStepState is completed / active / upcoming.
Cross-links
- Panels —
CdxSlideInPanelis the modal alternative toMasterDetailScaffold's overlay mode - Buttons —
Button.filledfor wizard "Next" actions - State Views —
AsyncViewinside each tab pane