adaptive_shell 2.0.0 copy "adaptive_shell: ^2.0.0" to clipboard
adaptive_shell: ^2.0.0 copied to clipboard

Adaptive master-detail layout for Flutter — automatically switches between compact and two-pane layouts across mobile, tablet, and desktop.

2.0.0 #

All changes are additive and backward compatible. Existing AdaptiveDestination(icon: Icons.people, label: '...') code compiles and runs with zero changes.

✨ New Features #

AdaptiveShellTheme — full navigation theming 🎨

A single const-able class that controls every visual aspect of the navigation chrome. Pass it to AdaptiveShell.theme:

AdaptiveShell(
  theme: const AdaptiveShellTheme(
    // Rail
    railMinExtendedWidth: 180,       // tighter default (was Flutter's 256)
    railBackgroundColor: Color(0xFFF8F9FA),
    railIndicatorColor: Color(0xFFD0BCFF),
    railSelectedLabelStyle: TextStyle(fontWeight: FontWeight.bold),
    railDecoration: BoxDecoration(
      border: Border(right: BorderSide(color: Color(0xFFE0E0E0))),
    ),
    // Nav bar
    navBarIndicatorColor: Color(0xFFD0BCFF),
    navBarLabelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
    navBarHeight: 72,
  ),
)

Supports copyWith and lerp.

AdaptiveShellController — programmatic rail control 🎛️

Collapse/expand the navigation rail from anywhere without prop-drilling:

final _nav = AdaptiveShellController();

AdaptiveShell(controller: _nav, ...)

// From an AppBar action or FAB:
IconButton(onPressed: _nav.toggleRail, icon: const Icon(Icons.menu))

Methods: collapseRail(), expandRail(), toggleRail(). Property: isRailCollapsed.

Widget icons for AdaptiveDestination — SVG, PNG, custom 🖼️

icon is now optional (IconData?). Use iconWidget, selectedIconWidget, or iconBuilder for full widget-based icons:

// Classic IconData — unchanged, still works
const AdaptiveDestination(icon: Icons.home, label: 'Home')

// SVG / PNG widget pair
AdaptiveDestination(
  iconWidget: SvgPicture.asset('assets/home.svg'),
  selectedIconWidget: SvgPicture.asset('assets/home_filled.svg'),
  label: 'Home',
)

// Builder — recommended for theme-driven SVG colors
AdaptiveDestination(
  iconBuilder: (context, isSelected) => SvgPicture.asset(
    'assets/home.svg',
    colorFilter: ColorFilter.mode(
      isSelected
        ? Theme.of(context).colorScheme.primary
        : Theme.of(context).colorScheme.onSurfaceVariant,
      BlendMode.srcIn,
    ),
  ),
  label: 'Home',
)

Custom nav builders 🏗️

Replace the entire NavigationBar or NavigationRail with your own widget:

AdaptiveShell(
  navigationBarBuilder: (context, destinations, index, onSelected) =>
      MyCustomBottomBar(...),
  navigationRailBuilder: (context, destinations, index, onSelected, isExtended) =>
      MyCustomSidebar(...),
)

More AdaptiveDestination fields

Field Description
iconSize: double? Wraps the resolved icon in a SizedBox for consistent SVG/PNG sizing
tooltip: String? Custom tooltip (defaults to label)
enabled: bool Disable individual destinations (grayed out, non-tappable)
badgeLabel: String? Flexible badge: "" = dot, "NEW", "99+"

🐛 Bug Fix #

  • Navigation rail on desktop mode now defaults to minExtendedWidth: 160 (previously fell back to Flutter's built-in 256 default, which consumed too much horizontal space on typical laptop windows).

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 current LayoutMode (usable in switch)
  • context.isCompact, context.isMedium, context.isExpanded — boolean checks
  • context.isMobile, context.isTablet, context.isDesktop — semantic aliases
  • context.isTwoPane — true when two panes are visible
  • context.adaptiveWidth(base) / context.adaptiveHeight(base) — scale dimensions
  • context.adaptivePadding() — responsive EdgeInsets (16 / 24 / 32)
  • context.adaptiveFontSize(base) / context.adaptiveSpacing(base) — scale text & gaps
  • context.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 #

  • AdaptiveShell refactored from StatelessWidget to StatefulWidget — fully backward-compatible.
  • onLayoutModeChanged now reliably fires across hot-reload and test pumps.
  • Fixed identical debug-overlay emoji for compact/medium modes.
  • AdaptiveMasterDetail._computeMode respects autoScale — navigation decisions (push vs in-place) now correctly match the visually rendered compact layout.
  • Fixed NavigationRail overflow when railCollapsible: true — the toggle button (~36 px) consumed height from the Column that also holds the NavigationRail, causing a RenderFlex overflowed by N pixels on the bottom assertion on short screens with many destinations. The rail is now wrapped in LayoutBuilder → SingleChildScrollView + ConstrainedBox(minHeight) + IntrinsicHeight so 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 AdaptiveMasterDetail pass-through.
  • 29 new tests for AdaptiveBuilder, keyboard shortcuts, collapsible rail, and custom paneDivider.
  • 3 new regression tests for the railCollapsible overflow fix (expanded + medium layouts, and default path).
  • Total: 139 tests, all passing.

⚠️ Breaking Changes #

  • None. All 1.0.x code compiles and runs unchanged.
  • New BuildContext extensions (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 with child1/child2 for full manual control over both panes, navigation, and state.
    • AdaptiveMasterDetail<T> — zero-boilerplate generic widget with itemBuilder/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.
  • AdaptiveBreakpoints with Material 3 window size class defaults + tabletFirst preset.
  • AdaptiveDestination with badge support.
  • emptyDetailPlaceholder for large screens with no selection.
  • masterHeader and masterBuilder for custom master pane layouts.
  • compactDetailScaffoldBuilder for custom mobile detail screen wrapping.
  • Animated pane transitions with configurable duration.
  • Full support for Android, iOS, web, macOS, Windows, Linux.
1
likes
160
points
75
downloads
screenshot

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Adaptive master-detail layout for Flutter — automatically switches between compact and two-pane layouts across mobile, tablet, and desktop.

Repository (GitHub)
View/report issues

Topics

#adaptive #responsive #layout #navigation #ui

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on adaptive_shell