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

An adaptive master-detail layout wrapper for Flutter. Automatically switches between BottomNavigationBar (mobile) and NavigationRail (tablet/web/desktop), showing child1 always and child2 beside it on [...]

adaptive_shell #

pub package License: BSD-3

An adaptive master-detail layout wrapper for Flutter. Supply child1 + child2 and the package handles everything: responsive pane layout, navigation mode switching, and layout-aware navigation — across mobile, tablet, web, and desktop.

Why adaptive_shell? #

Building adaptive layouts in Flutter typically requires hundreds of lines of boilerplate: checking screen widths, conditionally rendering NavigationBar vs NavigationRail, managing master-detail panes, and handling navigation differently per platform. adaptive_shell reduces this to a single wrapper widget.

flutter_adaptive_scaffold (the official package) has been discontinued. adaptive_shell is a simpler, actively-maintained alternative with a more intuitive API.

Screenshots #

Compact layout (Mobile) #

[Compact layout]

Expanded layout (Tablet/Desktop) #

[Expanded layout]

Features #

  • Single wrapper widget — just provide child1 + child2
  • Automatic navigationNavigationBar on mobile, NavigationRail on tablet/web
  • Master-detail splitchild1 always visible; child2 shown beside it on larger screens
  • Layout awarenessAdaptiveShell.of(context) returns the current mode so descendants can decide between pushing routes or updating state
  • Material 3 compliant — follows M3 window size classes (compact / medium / expanded)
  • Badge support — navigation destinations support notification counts
  • All platforms — Android, iOS, web, macOS, Windows, Linux

Layout behavior #

Compact (<600dp)          Medium (600–1200dp)         Expanded (>1200dp)
┌─────────────────┐       ┌──┬────────┬──────────┐    ┌─────────┬────────┬──────────┐
│                 │       │  │        │          │    │ Rail    │        │          │
│    child1       │       │R │ child1 │  child2  │    │(labels) │ child1 │  child2  │
│   (fullscreen)  │       │a │ (35%)  │  (65%)   │    │         │ (35%)  │  (65%)   │
│                 │       │i │        │          │    │         │        │          │
│                 │       │l │        │          │    │         │        │          │
├─────────────────┤       └──┴────────┴──────────┘    └─────────┴────────┴──────────┘
│  BottomNavBar   │
└─────────────────┘

Getting started #

Add to your pubspec.yaml:

dependencies:
  adaptive_shell: ^1.0.0

Usage #

Basic setup #

import 'package:adaptive_shell/adaptive_shell.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  int _navIndex = 0;
  Patient? _selected;

  void _onPatientTap(Patient patient) {
    // Check current layout to decide navigation strategy
    if (AdaptiveShell.of(context) == LayoutMode.compact) {
      // Mobile: push a new screen
      Navigator.push(context, MaterialPageRoute(
        builder: (_) => PatientDetailScreen(patient: patient),
      ));
    } else {
      // Tablet/web: update child2 in-place
      setState(() => _selected = patient);
    }
  }

  @override
  Widget build(BuildContext context) {
    return AdaptiveShell(
      destinations: const [
        AdaptiveDestination(icon: Icons.people, label: 'Patients'),
        AdaptiveDestination(icon: Icons.task, label: 'Tasks', badge: 3),
        AdaptiveDestination(icon: Icons.chat, label: 'Chat'),
      ],
      selectedIndex: _navIndex,
      onDestinationSelected: (i) => setState(() => _navIndex = i),
      child1: PatientListScreen(onTap: _onPatientTap),
      child2: _selected != null
        ? PatientDetailScreen(patient: _selected!)
        : null,
      emptyDetailPlaceholder: const Center(
        child: Text('Select a patient'),
      ),
    );
  }
}

Reading layout mode in descendants #

// In any descendant widget:
final mode = AdaptiveShell.of(context);
// Returns LayoutMode.compact, .medium, or .expanded

// Or use the convenience helper:
if (AdaptiveShell.isTwoPane(context)) {
  // Show selected highlight, hide chevron, etc.
}

Custom breakpoints #

AdaptiveShell(
  // Switch to two-pane earlier for tablet-first apps
  breakpoints: AdaptiveBreakpoints.tabletFirst,
  // ...
)

// Or fully custom:
AdaptiveShell(
  breakpoints: const AdaptiveBreakpoints(
    compact: 500,
    medium: 700,
    expanded: 960,
    masterRatio: 0.4, // 40% master, 60% detail
  ),
  // ...
)
AdaptiveShell(
  railLeading: FloatingActionButton.small(
    onPressed: _addPatient,
    child: const Icon(Icons.add),
  ),
  railTrailing: const Icon(Icons.settings),
  railBackgroundColor: Colors.grey.shade50,
  // ...
)

API reference #

AdaptiveShell #

Property Type Default Description
child1 Widget required Primary content, always visible
child2 Widget? null Detail content, shown on larger screens
destinations List<AdaptiveDestination> required Navigation items (min 2)
selectedIndex int required Currently selected nav index
onDestinationSelected ValueChanged<int> required Nav tap callback
breakpoints AdaptiveBreakpoints M3 defaults Breakpoint thresholds
showPaneDivider bool true Divider between panes
railLeading Widget? null Widget above rail items
railTrailing Widget? null Widget below rail items
railBackgroundColor Color? null Rail background
appBar PreferredSizeWidget? null Top app bar
transitionDuration Duration 300ms Pane animation speed
floatingActionButton Widget? null FAB widget
emptyDetailPlaceholder Widget? null Shown when child2 is null on large screens

AdaptiveBreakpoints #

Preset Compact Medium Expanded Master ratio
material3 600 840 1200 35%
tabletFirst 500 700 960 38%

LayoutMode #

Value Screen width Navigation Panes
compact < compact NavigationBar 1 (child1 only)
medium compact – expanded NavigationRail (icons) 2
expanded >= expanded NavigationRail (labels) 2

Design guidelines #

This package follows recommendations from:

Principles applied:

  • Uses LayoutBuilder, never checks device type or locks orientation
  • Material 3 window size classes for breakpoints
  • NavigationBar on compact, NavigationRail on medium/expanded
  • Preserves child widget state across layout changes
  • Supports keyboard, mouse, and touch input

Comparison with alternatives #

adaptive_shell flutter_adaptive_scaffold
Status Active Discontinued
API child1 / child2 Slot-based configs
Nav switching Automatic Manual per breakpoint
Layout awareness AdaptiveShell.of(context) None
Lines to set up ~20 ~100+

Additional information #

  • Issues: GitHub Issues
  • Contributing: PRs welcome. Please open an issue first for major changes.
  • License: BSD-3-Clause
1
likes
0
points
129
downloads

Publisher

unverified uploader

Weekly Downloads

An adaptive master-detail layout wrapper for Flutter. Automatically switches between BottomNavigationBar (mobile) and NavigationRail (tablet/web/desktop), showing child1 always and child2 beside it on larger screens. Follows Material 3 window size classes and Flutter adaptive design best practices

Repository (GitHub)
View/report issues

Topics

#adaptive #responsive #layout #navigation #ui

License

unknown (license)

Dependencies

flutter

More

Packages that depend on adaptive_shell