Skip to content
Blog

Navigation Drawers in Flutter: From Basics to Adaptive Patterns

Build Material navigation drawers with Scaffold, DrawerHeader, endDrawer, and an adaptive NavigationRail strategy for wide screens.

Published on • November 2, 2026

AI Assistant

Secondary navigation — settings, profile, saved items — needs somewhere to live without stealing horizontal space, and the Material navigation drawer is Flutter’s default answer (docs.flutter.dev).

The Basics: Scaffold.drawer

Scaffold(
  appBar: AppBar(title: const Text('Home')),
  drawer: const AppDrawer(),   // the app bar then shows the hamburger icon
)

Headers: DrawerHeader and UserAccountsDrawerHeader

Use DrawerHeader for branding, or UserAccountsDrawerHeader when you have a photo, name, and email:

Drawer(
  child: ListView(padding: EdgeInsets.zero, children: [
    const DrawerHeader(decoration: BoxDecoration(color: Colors.indigo),
        child: Text('RedlineSoft Blog', style: TextStyle(color: Colors.white, fontSize: 24))),
    ListTile(leading: const Icon(Icons.article_outlined),
        title: const Text('Articles'), onTap: () => _open(context, '/articles')),
    ListTile(leading: const Icon(Icons.settings_outlined),
        title: const Text('Settings'), onTap: () => _open(context, '/settings')),
  ]),
)

Controlling the Drawer: Close, Navigate, Open

An item must close first, then navigate — skipping Navigator.pop leaves the drawer open over the new route. endDrawer mirrors this on the right; for manual control use Scaffold.of(context):

void _open(BuildContext context, String route) {
  Navigator.pop(context);          // close the drawer
  Navigator.pushNamed(context, route);
}
final scaffold = Scaffold.of(context);
scaffold.isEndDrawerOpen ? scaffold.closeEndDrawer() : scaffold.openDrawer();

Adaptive Strategy: Drawer on Narrow, NavigationRail on Wide

LayoutBuilder(builder: (context, constraints) {
  final wide = constraints.maxWidth >= 800;
  return Scaffold(
    appBar: AppBar(title: const Text('Home')),
    drawer: wide ? null : const AppDrawer(),   // hide on wide screens
    body: Row(children: [
      if (wide)
        NavigationRail(
            selectedIndex: _index,
            onDestinationSelected: (i) => setState(() => _index = i),
            destinations: const [
              NavigationRailDestination(icon: Icon(Icons.article_outlined), label: Text('Articles')),
              NavigationRailDestination(icon: Icon(Icons.settings_outlined), label: Text('Settings')),
            ]),
      const Expanded(child: HomeBody()),
    ]),
  );
});

Gotchas

  • Navigator.pop before navigating — the single most common drawer bug.
  • Give every drawer ListTile a selected state tied to the current route, and handle inset padding (SafeArea or padding: EdgeInsets.zero) so content clears the status bar.

Takeaway: wire Scaffold.drawer for phones, swap in a NavigationRail past a width breakpoint, and always pop the drawer before you push the route.