Flutter Drawer: Build a Side Navigation Menu

Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter Drawer guide, with a side navigation menu sliding in, a user account header, and ListTile menu items.
Coding Liquids blog cover featuring Sagnik Bhattacharya for the Flutter Drawer guide.

The slide-in side menu — tap the hamburger, navigate, sign out — is a staple of apps with more destinations than a bottom bar can hold. Flutter makes it almost free: hand a Drawer to the Scaffold and it wires up the hamburger icon, the edge swipe, and the open/close animation for you. This guide covers adding the drawer, building a proper header, the menu items, closing the drawer on tap, the right-hand endDrawer, and opening it from a custom button, with paste-ready code.

The Complete Flutter Guide course thumbnail

The Complete Flutter Guide: Build Android, iOS and Web apps

Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.

Enrol now

Every snippet below is paste-ready against current stable Flutter. The drawer is the third navigation pattern, alongside bottom navigation and tabs — reach for it when you have more destinations than fit comfortably elsewhere.

Follow me on Instagram@sagnikteaches

We'll add a drawer, build the header, list the menu items, close the drawer on tap, and cover endDrawer and opening from code.

Connect on LinkedInSagnik Bhattacharya

If you'd rather watch a full app shell with a drawer built and themed, the channel covers these navigation structures end to end.

Subscribe on YouTube@codingliquids

Add a drawer

Pass a Drawer to the Scaffold's drawer property and Flutter adds the hamburger icon and the edge swipe for free. Fill it with a ListView so it scrolls if the menu is long.

Scaffold(
  appBar: AppBar(title: const Text('Coding Liquids')),
  drawer: Drawer(
    child: ListView(
      padding: EdgeInsets.zero,
      children: const [
        DrawerHeader(child: Text('Menu')),
        ListTile(leading: Icon(Icons.home), title: Text('Home')),
        ListTile(leading: Icon(Icons.settings), title: Text('Settings')),
      ],
    ),
  ),
  body: const HomeBody(),
)

Note padding: EdgeInsets.zero on the ListView — without it the header sits below an unwanted top gap, because ListView adds default padding inside the drawer.

A proper header

DrawerHeader gives you a styled top block, but for an account menu, UserAccountsDrawerHeader is purpose-built — it lays out an avatar, a name, and an email in the standard Material arrangement.

UserAccountsDrawerHeader(
  accountName: const Text('Sagnik Bhattacharya'),
  accountEmail: const Text('hello@codingliquids.com'),
  currentAccountPicture: const CircleAvatar(
    child: Text('S'),
  ),
)

This is the quickest way to a polished header. Use plain DrawerHeader with a gradient or logo when the menu isn't account-centric, and UserAccountsDrawerHeader when it is.

The Complete Flutter Guide course thumbnail

Build a full app navigation shell

The Complete Flutter Guide wires drawers, tabs, and bottom bars into real, shipped apps.

Enrol now

Close the drawer on tap

The open drawer is a route on the navigation stack, so a menu item must close it before — or as part of — navigating. Pop the drawer first inside the ListTile's onTap, then push the destination.

ListTile(
  leading: const Icon(Icons.settings),
  title: const Text('Settings'),
  onTap: () {
    Navigator.pop(context); // close the drawer
    Navigator.pushNamed(context, '/settings');
  },
)

Skip the pop and the drawer stays open over the new screen — a classic first-app bug. The pattern is always: close the drawer, then navigate, as covered in the navigation basics.

endDrawer and opening from code

The endDrawer property gives you a second drawer that opens from the right edge — handy for filters or a secondary panel. To open either drawer from your own button, call Scaffold.of(context).openDrawer() (or openEndDrawer()), making sure the context sits below the Scaffold.

Builder(
  builder: (context) => IconButton(
    icon: const Icon(Icons.menu),
    onPressed: () => Scaffold.of(context).openDrawer(),
  ),
)

The Builder matters: a context taken directly in the Scaffold's own build method is above the Scaffold, so Scaffold.of can't find it. Wrapping the button in a Builder — or using a GlobalKey<ScaffoldState> — gives you a context below it.

Common mistakes

  • Forgetting padding: EdgeInsets.zero. The ListView's default padding pushes the header down.
  • Not popping the drawer on tap. It stays open over the destination screen.
  • Scaffold.of with the wrong context. Use a Builder or a GlobalKey so the context is below the Scaffold.
  • A non-scrolling Column for many items. Use a ListView so long menus scroll.
  • Overloading the drawer. For three to five primary destinations, a bottom bar is usually clearer.

Frequently asked questions

How do I add a navigation drawer in Flutter?

Pass a Drawer to the Scaffold's drawer property; Flutter adds the hamburger icon and edge swipe automatically.

How do I close the drawer when a menu item is tapped in Flutter?

Call Navigator.pop(context) in the ListTile's onTap before navigating, since the open drawer is a route on the stack.

What is the difference between drawer and endDrawer in Flutter?

drawer opens from the left as the main menu; endDrawer opens from the right for secondary panels. A Scaffold can have both.

How do I open a Flutter drawer with a custom button?

Call Scaffold.of(context).openDrawer() from a context below the Scaffold — wrap the button in a Builder or use a GlobalKey<ScaffoldState>.

Further reads

Keep going with the tutorials that pair with this guide:

Sources: Flutter documentation — Drawer, DrawerHeader, UserAccountsDrawerHeader, Scaffold.drawer and endDrawer, and the "Add a drawer to a screen" cookbook recipe (docs.flutter.dev). Verified against current stable Flutter.