adaptive_shell 1.1.0
adaptive_shell: ^1.1.0 copied to clipboard
Adaptive master-detail layout for Flutter — automatically switches between compact and two-pane layouts across mobile, tablet, and desktop.
1.1.0 #
✨ New Features #
AdaptiveBuilder 🏗️
A standalone responsive builder widget that works without an AdaptiveShell ancestor — it uses LayoutBuilder and AdaptiveBreakpoints directly:
AdaptiveBuilder(
compact: (context) => MobileWidget(),
medium: (context) => TabletWidget(), // optional, falls back to compact
expanded: (context) => DesktopWidget(), // optional, falls back to medium
breakpoints: AdaptiveBreakpoints.tabletFirst, // customisable
)
Keyboard Shortcuts ⌨️
Map any ShortcutActivator (e.g. SingleActivator, LogicalKeySet) to a navigation destination index. Only active on medium/expanded layouts (tablet / desktop):
AdaptiveShell(
keyboardShortcuts: {
SingleActivator(LogicalKeyboardKey.digit1, control: true): 0,
SingleActivator(LogicalKeyboardKey.digit2, control: true): 1,
},
// ...
)
Collapsible Navigation Rail 📍
A chevron toggle button above the rail destinations lets users collapse the rail to icon-only mode. railCollapseOnMedium auto-collapses when the layout enters medium mode:
AdaptiveShell(
railCollapsible: true, // shows toggle button
railCollapseOnMedium: true, // auto-collapses on tablet breakpoint
// ...
)
Custom Pane Divider ➗
Replace the hardcoded VerticalDivider between the master and detail panes with any widget:
AdaptiveShell(
paneDivider: VerticalDivider(width: 2, color: Colors.blue),
// ...
)
Context Extensions 🔧
Quick access to layout information anywhere in your widget tree:
context.screenType/context.layoutMode— get currentLayoutMode(usable inswitch)context.isCompact,context.isMedium,context.isExpanded— boolean checkscontext.isMobile,context.isTablet,context.isDesktop— semantic aliasescontext.isTwoPane— true when two panes are visiblecontext.adaptiveWidth(base)/context.adaptiveHeight(base)— scale dimensionscontext.adaptivePadding()— responsiveEdgeInsets(16 / 24 / 32)context.adaptiveFontSize(base)/context.adaptiveSpacing(base)— scale text & gapscontext.adaptiveColumns— grid column count: 1 / 2 / 3 (compact / medium / expanded)context.adaptiveValue<T>(compact:, medium:, expanded:)— any typed value per breakpoint
// Boolean checks
if (context.isCompact) { /* mobile */ }
if (context.isTablet) { /* medium or expanded */ }
// Typed per-breakpoint value
final padding = context.adaptiveValue(compact: 8.0, medium: 16.0, expanded: 24.0);
// Grid columns
GridView.count(crossAxisCount: context.adaptiveColumns, ...)
// Layout mode in switch
switch (context.layoutMode) {
case LayoutMode.compact: return MobileNav();
case LayoutMode.medium: return TabletNav();
case LayoutMode.expanded: return DesktopNav();
}
Layout Change Callback
React to layout mode transitions for analytics or state management:
AdaptiveShell(
onLayoutModeChanged: (oldMode, newMode) {
print('Layout: $oldMode → $newMode');
},
// ...
)
Debug Overlay
Visual indicator showing current layout mode and breakpoints during development:
AdaptiveShell(
debugShowLayoutMode: true, // Shows overlay in top-right corner
// ...
)
AutoScale 📏
Proportionally scales your layout to fill any screen size — the same technique that made responsive_framework's AutoScale popular:
AdaptiveShell(
autoScale: true, // render at 360 dp design canvas, scale to screen
scaleFactor: 1.2, // optional: 20% boost on top of auto scale
autoScaleDesignWidth: 390, // optional: override canvas (e.g. iPhone 14 = 390 dp)
// ...
)
Default autoScaleDesignWidth is 360 dp — a typical phone canvas that ensures a compact (single-pane, bottom-nav) layout by default. The debug overlay gains a live ⚖️ Scale ×N.NN line when autoScale is enabled.
State Persistence 💾
Preserves scroll positions and widget state when the device rotates, the window resizes, or the layout mode changes:
AdaptiveShell(
persistState: true,
stateKey: 'my_shell', // optional namespace (default: 'adaptive_shell')
// ...
)
Uses stable GlobalKeys on child1/child2 so element subtrees survive compact ↔ wide transitions, plus a keyed PageStorage bucket for scroll restoration.
Animated Transitions ✨
Fine-tune detail-pane animations:
AdaptiveShell(
transitionCurve: Curves.easeInOutCubic, // custom curve (default: easeInOut)
enableHeroAnimations: true, // slide + fade instead of cross-fade
// ...
)
🔗 All new params available on AdaptiveMasterDetail #
autoScale, scaleFactor, autoScaleDesignWidth, persistState, stateKey, transitionCurve, enableHeroAnimations, paneDivider, railCollapsible, railCollapseOnMedium, keyboardShortcuts are all forwarded to the internal AdaptiveShell.
🐛 Bug Fixes & Internal Improvements #
AdaptiveShellrefactored fromStatelessWidgettoStatefulWidget— fully backward-compatible.onLayoutModeChangednow reliably fires across hot-reload and test pumps.- Fixed identical debug-overlay emoji for compact/medium modes.
AdaptiveMasterDetail._computeModerespectsautoScale— navigation decisions (push vs in-place) now correctly match the visually rendered compact layout.- Fixed
NavigationRailoverflow whenrailCollapsible: true— the toggle button (~36 px) consumed height from theColumnthat also holds theNavigationRail, causing aRenderFlex overflowed by N pixels on the bottomassertion on short screens with many destinations. The rail is now wrapped inLayoutBuilder → SingleChildScrollView + ConstrainedBox(minHeight) + IntrinsicHeightso destinations scroll gracefully when they exceed the available height, while still centering correctly on taller screens. The wrapper is applied only when the toggle is present; the default (railCollapsible: false) path is unchanged.
✅ Tests #
- 37 new tests for AutoScale, State Persistence, Animated Transitions, new context extensions, and
AdaptiveMasterDetailpass-through. - 29 new tests for AdaptiveBuilder, keyboard shortcuts, collapsible rail, and custom
paneDivider. - 3 new regression tests for the
railCollapsibleoverflow fix (expanded + medium layouts, and default path). - Total: 139 tests, all passing.
⚠️ Breaking Changes #
- None. All
1.0.xcode compiles and runs unchanged. - New
BuildContextextensions (layoutMode,adaptiveColumns,adaptiveValue, and all v1.1.0 additions) may conflict if you already define identically-named extensions. Resolve with a hide import:import 'package:adaptive_shell/adaptive_shell.dart' hide AdaptiveContextExtensions;
1.0.0 #
- Initial release with two APIs:
AdaptiveShell— flexible wrapper withchild1/child2for full manual control over both panes, navigation, and state.AdaptiveMasterDetail<T>— zero-boilerplate generic widget withitemBuilder/detailBuilder. Handles selection state, navigation (push on mobile, side-pane on tablet), and responsive layout switching automatically.
- Automatic
NavigationBar(compact) /NavigationRail(medium/expanded) switching. AdaptiveShell.of(context)for descendant layout-mode awareness.AdaptiveShell.isTwoPane(context)convenience helper.AdaptiveBreakpointswith Material 3 window size class defaults +tabletFirstpreset.AdaptiveDestinationwith badge support.emptyDetailPlaceholderfor large screens with no selection.masterHeaderandmasterBuilderfor custom master pane layouts.compactDetailScaffoldBuilderfor custom mobile detail screen wrapping.- Animated pane transitions with configurable duration.
- Full support for Android, iOS, web, macOS, Windows, Linux.
