Flutter: Drawer-Widget und Seitenmenü
Zuletzt aktualisiert:
5 Min. Lesezeit

Drawer ist das Seitenmenü, das vom Bildschirmrand hereingleitet. Es passt zu Apps mit mehr Bereichen, als eine untere Leiste fassen kann, oder zu Apps, die sekundäre Ziele wie Einstellungen, Hilfe und Konto an einem Ort sammeln wollen. Sobald Sie dem Scaffold einen drawer geben, erhält die AppBar ein Menü-Icon, die Wischgeste vom Rand funktioniert und der Zurück-Button schließt das Menü. Ihnen bleibt, den Inhalt zu füllen und festzulegen, was nach einer Auswahl passiert.
Live-Demo
Sie können dieses Widget im interaktiven Beispiel unten ausprobieren:
💡 Falls das Beispiel oben nicht lädt, klicken Sie auf DartPad, um es in einem neuen Tab auszuführen.
Grundlegende Verwendung
Scaffold(
appBar: AppBar(title: const Text('Drawer-Beispiel')),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(color: Colors.blue),
child: Text('Menütitel'),
),
ListTile(
leading: const Icon(Icons.home),
title: const Text('Startseite'),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('Einstellungen'),
onTap: () {
Navigator.pop(context);
},
),
],
),
),
body: const Center(child: Text('Inhalt')),
)Machen Sie eine ListView zum Standardinhalt: Wird das Menü länger oder das Gerät quer gehalten, gibt es keinen Overflow-Fehler. Navigator.pop(context) schließt hier den offenen Drawer, nicht die Seite.
DrawerHeader und UserAccountsDrawerHeader
DrawerHeader ist der frei gestaltbare Bereich oben im Menü; hinein darf jedes Widget. Wollen Sie Benutzername, E-Mail und Avatar zeigen, spart das fertige UserAccountsDrawerHeader Arbeit:
UserAccountsDrawerHeader(
accountName: const Text('Benutzername'),
accountEmail: const Text('[email protected]'),
currentAccountPicture: const CircleAvatar(child: Text('BN')),
decoration: const BoxDecoration(color: Colors.blue),
)Programmatisch öffnen und schließen
Um das Menü über einen eigenen Button zu öffnen, gibt es zwei Wege. Der erste: dem Scaffold einen GlobalKey geben.
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
Scaffold(
key: _scaffoldKey,
drawer: const Drawer(),
body: ElevatedButton(
onPressed: () => _scaffoldKey.currentState?.openDrawer(),
child: const Text('Drawer öffnen'),
),
)Der zweite ist Scaffold.of(context).openDrawer(). Dabei lauert eine context-Falle, die ich unten bei den Fehlern erkläre. Zum Schließen dient Navigator.pop(context) oder _scaffoldKey.currentState?.closeDrawer().
EndDrawer (rechte Seite)
endDrawer öffnet dasselbe Widget vom rechten Rand. Während das linke Menü der Navigation dient, trägt die rechte Seite meist screenbezogene Inhalte wie ein Filterpanel. Geöffnet wird mit openEndDrawer().
Wichtige Eigenschaften
| Eigenschaft | Beschreibung |
|---|---|
drawer |
Drawer auf der linken Seite |
endDrawer |
Drawer auf der rechten Seite |
drawerScrimColor |
Farbe, die den Hintergrund bei offenem Menü abdunkelt |
drawerEdgeDragWidth |
Breite des Bereichs, in dem die Randgeste erkannt wird |
drawerEnableOpenDragGesture |
Schaltet das Öffnen per Wischen ein oder aus |
Drawer(width: ...) |
Breite des Menüs |
Beachten Sie: Das sind Parameter des Scaffold, nicht des Drawer – mit Ausnahme der Breite.
Material 3: NavigationDrawer
Der mit Material 3 eingeführte NavigationDrawer hebt den gewählten Eintrag mit einer pillenförmigen Markierung hervor und steuert die Auswahl über selectedIndex und onDestinationSelected. Statt ListTiles von Hand zu stylen, listen Sie Ziele auf:
NavigationDrawer(
selectedIndex: _index,
onDestinationSelected: (index) {
Navigator.pop(context);
setState(() => _index = index);
},
children: const [
Padding(
padding: EdgeInsets.fromLTRB(28, 16, 16, 10),
child: Text('Menü'),
),
NavigationDrawerDestination(
icon: Icon(Icons.inbox_outlined),
selectedIcon: Icon(Icons.inbox),
label: Text('Posteingang'),
),
NavigationDrawerDestination(
icon: Icon(Icons.send_outlined),
selectedIcon: Icon(Icons.send),
label: Text('Gesendet'),
),
],
)In children dürfen auch Widgets stehen, die keine Ziele sind, etwa eine Überschrift oder ein Divider; der Index zählt nur die NavigationDrawerDestinations. Nach einer Auswahl schließt sich das Menü nicht von selbst, Navigator.pop bleibt Ihre Aufgabe.
Wann verwenden – und wann nicht?
Ein Drawer passt zu Apps mit mehr als fünf Hauptbereichen oder zu Apps, die selten genutzte Ziele (Einstellungen, Info, Abmelden) aus dem Blickfeld halten wollen. Der Preis: Das Menü ist versteckt. Der Nutzer muss erst auf das Icon tippen, um die Bereiche überhaupt zu sehen.
- Bei drei bis fünf gleich wichtigen Bereichen ist eine BottomNavigationBar besser; die Ziele sind immer sichtbar und einen Tipp entfernt.
- Für gleichrangige Inhalte desselben Screens (Aktiv / Abgeschlossen) nehmen Sie eine TabBar, keinen Drawer.
- Auf Tablet- und Desktop-Breite ist eine
NavigationRailoder ein dauerhaftes Seitenpanel einem versteckten Menü überlegen. - Kombinieren Sie untere Leiste und Drawer, gehört dasselbe Ziel nicht an beide Orte: Hauptbereiche unten, sekundäre Ziele in den Drawer.
Häufige Fehler
1. Scaffold.of() called with a context that does not contain a Scaffold. Dieser Fehler erscheint, wenn Sie Scaffold.of mit dem context der build-Methode aufrufen, die das Scaffold erst erzeugt. Dieser context liegt im Baum oberhalb des Scaffold und kann nicht nach unten suchen. Mit einem Builder erhalten Sie einen neuen context unterhalb des Scaffold:
Scaffold(
drawer: const Drawer(),
body: Builder(
builder: (innerContext) => ElevatedButton(
onPressed: () => Scaffold.of(innerContext).openDrawer(),
child: const Text('Menü öffnen'),
),
),
)Den Button in eine eigene Widget-Klasse auszulagern wirkt genauso, denn deren build-Methode bekommt ihren eigenen context.
2. Navigieren, ohne das Menü zu schließen. Symptom: Kehrt der Nutzer von der geöffneten Seite zurück, steht der Drawer noch offen. Schließen Sie im onTap zuerst das Menü und navigieren Sie danach:
onTap: () {
Navigator.pop(context); // schließt den Drawer
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const SettingsPage()),
);
}3. Ein leerer Streifen über dem Header. ListView fügt standardmäßig oben einen Abstand in Höhe der Statusleiste ein; die Farbe des DrawerHeader reicht deshalb nicht bis an den oberen Rand. padding: EdgeInsets.zero behebt das.
4. Horizontal scrollender Inhalt öffnet das Menü. Enthält die Seite eine Karte, eine horizontale Liste oder eine wischbare Galerie, kann eine Geste am linken Rand den Drawer auslösen. Mit drawerEnableOpenDragGesture: false am Scaffold öffnet sich das Menü nur noch über das Icon.
Mini-Szenario: Menü, das Bereiche wechselt und Einstellungen öffnet
Stellen Sie sich ein Lernportal vor: Kurse, Aufgaben und Noten tauschen den Body innerhalb desselben Scaffold, während die Einstellungen als eigene Seite aufgehen. Beide Verhaltensweisen unterscheiden sich, und das Menü muss beide richtig abbilden:
class HomeShell extends StatefulWidget {
const HomeShell({super.key});
@override
State<HomeShell> createState() => _HomeShellState();
}
class _HomeShellState extends State<HomeShell> {
int _index = 0;
static const _titles = ['Kurse', 'Aufgaben', 'Noten'];
static const _icons = [Icons.school, Icons.assignment, Icons.grade];
void _selectSection(int index) {
Navigator.pop(context); // zuerst schließt der Drawer
setState(() => _index = index);
}
void _openSettings() {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const SettingsPage()),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(_titles[_index])),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(color: Colors.indigo),
child: Text(
'Lernportal',
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
for (var i = 0; i < _titles.length; i++)
ListTile(
leading: Icon(_icons[i]),
title: Text(_titles[i]),
selected: i == _index,
onTap: () => _selectSection(i),
),
const Divider(),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('Einstellungen'),
onTap: _openSettings,
),
],
),
),
body: Center(child: Text('Inhalt: ${_titles[_index]}')),
);
}
}
class SettingsPage extends StatelessWidget {
const SettingsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Einstellungen')),
body: const Center(child: Text('Einstellungen')),
);
}
}Bei der Bereichswahl öffnet sich keine neue Seite, es ändert sich nur _index; der Zurück-Button verlässt deshalb die App, statt durch die Bereiche zu blättern. selected: i == _index zeigt dem Nutzer, wo er sich befindet. Die Einstellungen dagegen sind ein echter Seitenwechsel: Ihre AppBar bekommt einen Zurück-Pfeil, und bei der Rückkehr ist das Menü geschlossen, weil zuerst pop aufgerufen wurde.
Häufig gestellte Fragen
Wie öffne ich einen Drawer auf der rechten Seite?
Mit dem Parameter endDrawer des Scaffold. Ist actions leer, ergänzt die AppBar das Menü-Icon rechts von selbst; über einen eigenen Button öffnen Sie mit openEndDrawer().
Wie ändere ich das Drawer-Icon?
Setzen Sie einen eigenen IconButton in den Bereich leading der AppBar und rufen Sie in onPressed Scaffold.of(context).openDrawer() auf. Umschließen Sie den Button mit einem Builder, damit der context von unterhalb des Scaffold stammt.
Was ist der Unterschied zwischen NavigationDrawer und Drawer?
Drawer ist ein leeres Panel; Inhalt und Auswahl-Optik bauen Sie selbst. NavigationDrawer bringt das Material-3-Aussehen samt Hervorhebung des gewählten Eintrags mit und verwaltet die Auswahl über einen Index.
Was macht der Zurück-Button bei geöffnetem Drawer?
Er schließt zuerst das Menü, die Seite bleibt bestehen. Ist das Menü geschlossen, greift das normale Zurück-Verhalten.
Verwandte Artikel
Flutter: TabBar und TabBarView verwenden
TabBar und TabBarView in Flutter: DefaultTabController oder eigener TabController, Tab-Zustand erhalten, Material-3-Einstellungen und häufige Fehler.
Flutter: AppBar-Widget und Anpassung
AppBar in Flutter richtig einsetzen: title, leading, actions und bottom, der Farbwechsel beim Scrollen in Material 3 und Lösungen für häufige Fehler.
Flutter: BottomNavigationBar im Vergleich zu NavigationBar
Von BottomNavigationBar zur Material-3-NavigationBar: Zuordnungstabelle, labelBehavior, NavigationBarThemeData, Entscheidungshilfe und adaptive Shell.