İçeriğe geç / Skip to content / Zum Inhalt

Flutter: AppBar-Widget und Anpassung

Ahmet Balaman

Zuletzt aktualisiert:

5 Min. Lesezeit

FlutterAppBarMaterial 3NavigationWidgetUI
Flutter: AppBar-Widget und Anpassung

AppBar ist die obere Leiste, die Sie dem Parameter appBar eines Scaffold übergeben. Sie zeigt, auf welchem Screen man sich befindet, trägt den Zurück- oder Menübutton und bündelt rechts die Aktionen des Screens (Suchen, Teilen, Löschen). Am Anfang reicht ein title; wächst der Screen, kommen leading, actions und bottom dazu. Dieser Beitrag geht diese Bereiche durch, erklärt das Farbverhalten von Material 3 und zeigt die Fehler, die am häufigsten auftreten.

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

Die Teile eines Scaffold: appBar oben, body in der Mitte, floatingActionButton unten rechts, bottomNavigationBar darunter, drawer links

Scaffold(
  appBar: AppBar(
    title: const Text('Seitentitel'),
  ),
  body: const Center(child: Text('Inhalt')),
)

Liegt die AppBar in einem Scaffold, werden Statusleisten-Abstand, Höhe und Zurück-Button für Sie berechnet. Eine per Navigator geöffnete Seite bekommt automatisch einen Zurück-Pfeil, ein Scaffold mit drawer ein Menü-Icon.

Wichtige Eigenschaften

Eigenschaft Beschreibung
title Titel-Widget (meist ein Text)
leading Widget links (Zurück-Button, Menü-Icon)
actions Liste der Buttons rechts
bottom Bereich unterhalb der Leiste (TabBar, Fortschrittsbalken)
backgroundColor Hintergrundfarbe
foregroundColor Farbe von Titel und Icons
elevation / shadowColor Stärke und Farbe des Schattens
scrolledUnderElevation Elevation, sobald Inhalt unter die Leiste scrollt
centerTitle Zentriert den Titel
automaticallyImplyLeading Schaltet den automatischen Zurück-/Menübutton ein oder aus
toolbarHeight Höhe der Leiste

Actions verwenden

actions ist eine Liste von Widgets. Bleiben Sie bei zwei, höchstens drei Icons und verschieben Sie den Rest in ein Überlaufmenü. Genau dafür gibt es den PopupMenuButton:

AppBar(
  title: const Text('Titel'),
  actions: [
    IconButton(
      icon: const Icon(Icons.search),
      tooltip: 'Suchen',
      onPressed: () {},
    ),
    PopupMenuButton<String>(
      onSelected: (value) {},
      itemBuilder: (context) => const [
        PopupMenuItem(value: 'settings', child: Text('Einstellungen')),
        PopupMenuItem(value: 'help', child: Text('Hilfe')),
      ],
    ),
  ],
)

Lassen Sie tooltip nicht weg: Bei langem Druck erscheint ein Hinweis, und Screenreader lesen den Button mit diesem Text vor.

Leading anpassen

Sobald Sie leading selbst belegen, ist der automatische Zurück- und Menübutton abgeschaltet, und das Verhalten liegt bei Ihnen. Zum Öffnen des Menüs brauchen Sie Scaffold.of(context), und dieser Aufruf muss mit einem context unterhalb des Scaffold erfolgen. Deshalb der Builder:

AppBar(
  leading: Builder(
    builder: (context) => IconButton(
      icon: const Icon(Icons.menu),
      onPressed: () => Scaffold.of(context).openDrawer(),
    ),
  ),
  title: const Text('Titel'),
)

Diese Falle beschreibe ich ausführlich im Drawer-Beitrag. Setzen Sie statt eines Icons einen Textbutton wie „Abbrechen“ ein, wird der Platz zu schmal; mit leadingWidth verbreitern Sie ihn.

Der Bereich bottom

bottom erwartet ein PreferredSizeWidget, also ein Widget, das seine Höhe vorab mitteilt. TabBar implementiert das bereits und wandert auf Screens mit Tabs direkt in bottom. Ein eigenes Widget verpacken Sie in PreferredSize:

AppBar(
  title: const Text('Bestellungen'),
  bottom: const PreferredSize(
    preferredSize: Size.fromHeight(4),
    child: LinearProgressIndicator(),
  ),
)

Suchfeld und Farbverlauf

Da title jedes Widget annimmt, funktioniert dort auch ein TextField. Für einen Farbverlauf im Hintergrund nutzen Sie flexibleSpace; dieser Bereich wird hinter der Leiste gezeichnet:

AppBar(
  title: const Text('Gradient'),
  foregroundColor: Colors.white,
  flexibleSpace: Container(
    decoration: const BoxDecoration(
      gradient: LinearGradient(
        colors: [Colors.purple, Colors.blue],
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
      ),
    ),
  ),
)

Farbe und Schatten in Material 3

In Material 3 hat die AppBar standardmäßig die Farbe colorScheme.surface und keinen Schatten. Scrollt Inhalt unter die Leiste, wechselt sie in den Zustand „scrolled under“: scrolledUnderElevation greift, und die Fläche verschiebt sich um einen Farbton. Je nach Flutter-Version zeigt sich das als Surface-Tint-Überlagerung oder als anderer Surface-Ton. Soll die Leiste in Ihrem Design immer gleich aussehen, legen Sie das im Theme fest:

final scheme = ColorScheme.fromSeed(seedColor: Colors.indigo);

ThemeData(
  colorScheme: scheme,
  appBarTheme: AppBarTheme(
    backgroundColor: scheme.surface,
    foregroundColor: scheme.onSurface,
    scrolledUnderElevation: 0,
    surfaceTintColor: Colors.transparent,
  ),
)

Der zweite Vorteil von AppBarTheme: Sie wiederholen dieselben Farben nicht mehr auf jedem Screen.

Wann verwenden – und wann nicht?

Für jeden Standard-Screen mit Titel und ein paar Aktionen ist AppBar die richtige Wahl. In diesen Fällen lohnt der Blick auf etwas anderes:

  • Soll der Kopfbereich beim Scrollen schrumpfen, ein großes Bild tragen oder beim Hochscrollen zurückkehren, brauchen Sie SliverAppBar. Das behandle ich in SliverAppBar mit Kategorie-Header und Suchfeld.
  • Vollbild-Grafiken, Onboarding oder ein Videoplayer kommen oft ganz ohne Leiste aus; lassen Sie appBar leer und schützen Sie den Inhalt mit SafeArea.
  • Soll der Inhalt durch die Leiste hindurch sichtbar sein, setzen Sie am Scaffold extendBodyBehindAppBar: true und verwenden backgroundColor: Colors.transparent.
  • Gibt es genau eine Hauptaktion und ist sie die zentrale Aufgabe des Screens, machen Sie daraus einen FloatingActionButton, statt sie in actions zu verstecken.

Häufige Fehler

1. Die Leiste wechselt beim Scrollen die Farbe. Symptom: Beim Öffnen ist die Leiste weiß, nach kurzem Scrollen wirkt sie gräulich oder eingefärbt. Das ist kein Bug, sondern das oben beschriebene Material-3-Verhalten. Wenn Sie es nicht möchten, setzen Sie backgroundColor ausdrücklich und ergänzen scrolledUnderElevation: 0.

2. elevation gesetzt, aber kein Schatten sichtbar. Die Standard-Schattenfarbe in Material 3 ist transparent. Wer einen Schatten will, nennt auch dessen Farbe:

AppBar(
  title: const Text('Mit Schatten'),
  elevation: 4,
  shadowColor: Colors.black54,
)

3. Hintergrund geändert, Titel und Icons nicht mehr lesbar. Ein dunkles backgroundColor, während der Text in seiner dunklen Standardfarbe bleibt, kommt sehr oft vor. Statt jedes Text einzeln zu stylen, nutzen Sie foregroundColor; es färbt Titel, Zurück-Pfeil und die Icons in actions gemeinsam:

AppBar(
  title: const Text('Profil'),
  backgroundColor: Colors.indigo,
  foregroundColor: Colors.white,
)

4. Nach eigenem leading ist der Zurück-Button weg. Ein gesetztes leading schaltet den automatischen Button vollständig ab. Schreiben Sie das Zurück-Verhalten (Navigator.pop(context)) selbst, oder übergeben Sie leading nur bei Bedarf und lassen es sonst null. Genau das macht das folgende Szenario.

Mini-Szenario: Mehrfachauswahl

Stellen Sie sich eine Notizliste vor. Ein langer Druck auf eine Notiz startet den Auswahlmodus: Der Titel zeigt die Anzahl, links erscheint ein Schließen-Button, rechts ein Löschen-Button. Endet die Auswahl, sieht die Leiste wieder normal aus. Eine zweite AppBar ist dafür nicht nötig; dieselbe Leiste füllt ihre Bereiche je nach Zustand:

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

  @override
  State<NotesPage> createState() => _NotesPageState();
}

class _NotesPageState extends State<NotesPage> {
  final List<String> _notes = List.generate(20, (i) => 'Notiz ${i + 1}');
  final Set<String> _selected = {};

  bool get _selectionMode => _selected.isNotEmpty;

  void _toggle(String note) {
    setState(() {
      if (!_selected.remove(note)) _selected.add(note);
    });
  }

  void _deleteSelected() {
    setState(() {
      _notes.removeWhere(_selected.contains);
      _selected.clear();
    });
  }

  @override
  Widget build(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;

    return Scaffold(
      appBar: AppBar(
        backgroundColor: _selectionMode ? scheme.primaryContainer : null,
        leading: _selectionMode
            ? IconButton(
                icon: const Icon(Icons.close),
                tooltip: 'Auswahl aufheben',
                onPressed: () => setState(_selected.clear),
              )
            : null,
        title: Text(
          _selectionMode ? '${_selected.length} ausgewählt' : 'Meine Notizen',
        ),
        actions: _selectionMode
            ? [
                IconButton(
                  icon: const Icon(Icons.delete_outline),
                  tooltip: 'Löschen',
                  onPressed: _deleteSelected,
                ),
              ]
            : [
                IconButton(
                  icon: const Icon(Icons.search),
                  tooltip: 'Suchen',
                  onPressed: () {},
                ),
              ],
      ),
      body: ListView.builder(
        itemCount: _notes.length,
        itemBuilder: (context, index) {
          final note = _notes[index];
          return ListTile(
            title: Text(note),
            selected: _selected.contains(note),
            onLongPress: () => _toggle(note),
            onTap: _selectionMode ? () => _toggle(note) : null,
          );
        },
      ),
    );
  }
}

Zwei Details sind wichtig. leading bleibt außerhalb des Auswahlmodus null; wird die Seite über den Navigator geöffnet, erscheint der Zurück-Pfeil also weiterhin von selbst. Bei backgroundColor gilt dasselbe: null fällt auf den Theme-Wert zurück. Der Nutzer erkennt den Moduswechsel an der Farbe, und Sie pflegen keine zwei getrennten Leisten.

Häufig gestellte Fragen

Wie ändere ich die Höhe der AppBar?

Mit dem Parameter toolbarHeight. Verwenden Sie zusätzlich bottom, kommt dessen Höhe noch hinzu.

Wie entferne ich den Zurück-Button?

Setzen Sie automaticallyImplyLeading: false. Das blendet nur den Button aus; die Zurück-Geste des Android-Systems funktioniert weiter, und um sie zu blockieren, brauchen Sie PopScope.

Wie verwende ich denselben AppBar-Stil auf allen Screens?

Übergeben Sie ThemeData im Feld appBarTheme ein AppBarTheme. Farben, centerTitle, scrolledUnderElevation und ähnliche Einstellungen gelten dann für jede AppBar; ein Parameter an einer einzelnen AppBar überschreibt das Theme weiterhin.

Was ist der Unterschied zwischen AppBar und SliverAppBar?

AppBar hat eine feste Höhe und wird dem Scaffold übergeben. SliverAppBar lebt in einem CustomScrollView und kann sich beim Scrollen ausdehnen, zusammenziehen, verschwinden und zurückkehren.

Kommentare