Migrating off the discontinued flutter_adaptive_scaffold - keeping go_router and your state
Google discontinued flutter_adaptive_scaffold in April 2025 - not because it was unpopular (it still pulls thousands of downloads a week), but to refocus on core framework work. The ask was for the community to rally around one sustainable fork instead of a dozen one-offs.
Most of the forks that appeared renamed the package and added cosmetic options - but the ones I found didn't address the two things you actually hit in a real app:
- go_router integration. The original predated
StatefulShellRoute, so wiring adaptive navigation to a router meant a pile of glue code (tracked upstream as flutter#129850). - State loss on resize. The original assigned a different body slot/key per breakpoint, so dragging a desktop window narrow - or a foldable unfolding - rebuilt your whole body subtree, wiping scroll position, form input and in-memory state.
adaptive_scaffold_router is a drop-in successor that fixes both.
Migrating is a one-line import change
The core API (AdaptiveScaffold, AdaptiveLayout, SlotLayout, Breakpoint, Breakpoints) is compatible with flutter_adaptive_scaffold 0.3.x. Swap the dependency and the import:
dependencies:
adaptive_scaffold_router: ^0.3.1
# was: flutter_adaptive_scaffold
import 'package:adaptive_scaffold_router/adaptive_scaffold_router.dart';
Existing AdaptiveScaffold(...) code keeps compiling.
The upgrade: wire it to go_router
Instead of hand-rolling glue, hand a StatefulNavigationShell to AdaptiveNavigationShell:
final router = GoRouter(
initialLocation: '/inbox',
routes: [
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) => AdaptiveNavigationShell(
navigationShell: navigationShell,
destinations: const [
NavigationDestination(icon: Icon(Icons.inbox), label: 'Inbox'),
NavigationDestination(icon: Icon(Icons.article), label: 'Articles'),
NavigationDestination(icon: Icon(Icons.settings), label: 'Settings'),
],
),
branches: [
StatefulShellBranch(routes: [GoRoute(path: '/inbox', builder: ...)]),
StatefulShellBranch(routes: [GoRoute(path: '/articles', builder: ...)]),
StatefulShellBranch(routes: [GoRoute(path: '/settings', builder: ...)]),
],
),
],
);
That's the whole adaptive shell. On a phone you get a bottom navigation bar; on a tablet, a navigation rail; on desktop, an extended rail. Each tab keeps its own navigation stack via go_router branches.
Why state survives a resize
Under the hood the shell feeds one body to AdaptiveScaffold (not a per-breakpoint body), so the underlying SlotLayout keeps a single stable key for every width. Changing the breakpoint swaps only the navigation chrome - the body subtree is never torn down.
Drag the window from desktop to phone width mid-scroll and the scroll position (and any half-filled form) is exactly where you left it. That's the bug the original couldn't fix without a key-per-breakpoint redesign.
New in 0.3.0: the full Material 3 progression
Material 3's adaptive navigation guidance is bottom bar → rail → drawer as the window widens. Set one flag and the largest breakpoints render a permanent NavigationDrawer instead of the extended rail:
AdaptiveNavigationShell(
navigationShell: navigationShell,
permanentDrawer: true,
destinations: destinations,
)
The rail still shows at medium widths, and - because the drawer lives in the navigation slot, not the body - switching between rail and drawer preserves branch state too.
Also in the box
- Cupertino / platform-adaptive bottom bar -
bottomNavigationBuilder(orcupertino: true) swaps the small-breakpoint bar for aCupertinoTabBaron Apple platforms. - Badges - compose a Material
Badgestraight into a destination's icon. - Rail header / footer -
leadingExtendedNavRailandtrailingNavRail. - Two-pane body +
secondaryBodyfor list/detail, foldable-aware.
160/160 pub points, BSD-3 licensed, actively maintained. Issues and PRs welcome on GitHub.
If you've got a flutter_adaptive_scaffold app gathering dust on a pinned version, this is the path forward - change the import, optionally add the shell, and you're on a maintained package again.
Comments
No comments yet. Start the discussion.