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

Flutter: Innenabstände mit Padding steuern

Ahmet Balaman

Zuletzt aktualisiert:

6 Min. Lesezeit

FlutterPaddingEdgeInsetsLayoutSafeAreaUI
Flutter: Innenabstände mit Padding steuern

Padding ist ein Widget mit genau einer Aufgabe: Es lässt Platz rund um sein Kind frei. Dazu zieht es die EdgeInsets von den erhaltenen Constraints ab, layoutet das Kind im verbleibenden Raum und verschiebt es um den angegebenen Betrag nach innen. Dass Karteninhalte nicht am Rand kleben, eine Listenzeile Luft bekommt oder eine Seite 16 Pixel vom Bildschirmrand entfernt bleibt, ist alles die Arbeit dieses Widgets. In diesem Beitrag zeige ich alle EdgeInsets-Konstruktoren, EdgeInsetsDirectional für Sprachen von rechts nach links, das Zusammenspiel mit SafeArea und Tastatur-Insets sowie die Fehler, die Ihnen in der Konsole am häufigsten begegnen.

Grundlegende Verwendung

Padding(
  padding: const EdgeInsets.all(16),
  child: Text('Um mich herum sind 16 Pixel Abstand'),
)

Wie die offizielle API-Dokumentation des Padding-Widgets betont, ist Padding selbst unsichtbar; es hat keine Hintergrundfarbe und fügt keine Tap-Fläche hinzu. Es verkleinert nur den Raum für sein Kind und verschiebt es. Wenn Sie sich fragen, wo der Abstand geblieben ist, ist der schnellste Weg, das Padding im Flutter Inspector auszuwählen und die blau schraffierte Fläche anzusehen.

Die EdgeInsets-Konstruktoren

Abstandswerte werden mit der Klasse EdgeInsets angegeben. Es gibt vier Konstruktoren und eine Konstante; welchen Sie wählen, ist eine Frage der Lesbarkeit, das Ergebnis ist dasselbe.

Die vier Abstandsvarianten von EdgeInsets.all, symmetric, only und fromLTRB an derselben Box

// Alle vier Seiten gleich
const EdgeInsets.all(16)

// Horizontal (links+rechts) und vertikal (oben+unten) getrennt
const EdgeInsets.symmetric(horizontal: 16, vertical: 8)

// Nur die genannten Seiten; der Rest ist 0
const EdgeInsets.only(left: 16, top: 8)

// Links, oben, rechts, unten in dieser Reihenfolge (Left, Top, Right, Bottom)
const EdgeInsets.fromLTRB(16, 8, 16, 12)

// Gar kein Abstand
EdgeInsets.zero

Wenn sich die Werte nie ändern, schreiben Sie const; dann legt der Widget-Baum nicht bei jedem Build ein neues EdgeInsets-Objekt an. fromLTRB reiht vier nackte Zahlen aneinander und ist am schwersten zu lesen; selbst wenn drei oder vier Seiten unterschiedlich sind, sind die benannten Parameter von only die bessere Wahl.

left und right sind physische Richtungen. Läuft Ihre App in einer Sprache, die von rechts nach links geschrieben wird (RTL), etwa Arabisch oder Hebräisch, bedeutet „16 Pixel am Textanfang" die rechte Kante, und left trifft die falsche Seite. Wie die API-Dokumentation zu EdgeInsetsDirectional beschreibt, arbeitet EdgeInsetsDirectional mit start und end und spiegelt sich anhand der umgebenden Directionality selbst:

Padding(
  padding: const EdgeInsetsDirectional.only(start: 16, end: 8),
  child: Text('In LTR 16 links, in RTL 16 rechts'),
)

Auch EdgeInsetsDirectional.all, .symmetric und .fromSTEB (Start, Top, End, Bottom) stehen zur Verfügung. Wenn Ihre App nur in einer Links-nach-rechts-Sprache erscheint, reicht das physische EdgeInsets; in einer mehrsprachigen App erspart Ihnen die Directional-Variante überall dort, wo der horizontale Abstand asymmetrisch ist, ein späteres Umschreiben.

Padding, Container(padding) und SizedBox

Drei verschiedene Werkzeuge haben mit Abstand zu tun und werden gern verwechselt:

  • Padding: Abstand rund um ein einzelnes Kind. Die leichteste Option.
  • Container(padding: ...): Steht ohnehin ein Container für Farbe, Rahmen oder abgerundete Ecken da, nutzen Sie dessen Parameter padding. Container erzeugt daraus intern sowieso ein Padding-Widget; ein zusätzlich eingepacktes Padding funktioniert, verschachtelt aber unnötig.
  • SizedBox(height: 16): eine Lücke zwischen zwei Geschwister-Widgets. Um in einer Column Abstand zwischen zwei Texten zu schaffen, nehmen Sie SizedBox statt Padding; Padding umschließt ein Kind, es ist kein Trenner zwischen Geschwistern. Mehr dazu im Beitrag zu SizedBox.

Dann gibt es noch margin: Der Parameter margin des Container ist Außenabstand, die Hintergrundfarbe beginnt also erst nach der Lücke. padding ist Innenabstand, der Hintergrund deckt die Lücke mit ab. Ein farbiger Container zeigt den Unterschied am deutlichsten.

SafeArea und System-Insets

Notch, Statusleiste und die untere Navigationsleiste verdecken einen Teil des Bildschirms. SafeArea ist ein spezialisiertes Widget, das genau so viel Padding hinzufügt:

Scaffold(
  body: SafeArea(
    child: Padding(
      padding: const EdgeInsets.all(16),
      child: Column(children: [Text('Im sicheren Bereich')]),
    ),
  ),
)

Wenn Sie selbst auslesen möchten, wie viel das System verdeckt, liefert MediaQuery.paddingOf(context) ein EdgeInsets; MediaQuery.paddingOf(context).bottom ist zum Beispiel die Höhe der unteren Navigationsleiste. Den von der Tastatur belegten Bereich lesen Sie über MediaQuery.viewInsetsOf(context).bottom. Meist übernimmt das Scaffold das über resizeToAvoidBottomInset für Sie; bauen Sie aber außerhalb eines Scaffold ein eigenes unteres Panel, müssen Sie diesen Wert zum Padding addieren. Wie Sie Bildschirmmaße mit MediaQuery auslesen, steht im Beitrag zu MediaQuery.

Wann verwenden – und wann nicht?

Greifen Sie zu Padding:

  • Wenn der Inhalt eines einzelnen Widgets von dessen Rändern weg soll.
  • Um den Seiteninhalt vom Bildschirmrand abzusetzen (EdgeInsets.all(16) oder symmetric(horizontal: 16)).
  • Für den Innenabstand von Containern wie Karten, Listenzeilen und Dialogen.

Verwenden Sie Padding nicht:

  • Für Abstand zwischen Geschwistern: SizedBox (oder in aktuellen Flutter-Versionen der Parameter spacing von Row und Column) ist sauberer.
  • Für den Randabstand einer ListView: nutzen Sie ListView(padding: ...). Packen Sie die ListView von außen in ein Padding, schrumpft auch der Scrollbereich; Elemente werden an einer harten Linie abgeschnitten, statt unter den Rand zu gleiten.
  • Für Abstand innerhalb eines Buttons: Stilparameter wie ElevatedButton.styleFrom(padding: ...) vergrößern die Tap-Fläche; ein außen herumgelegtes Padding tut das nicht.
  • Für negativen Abstand: EdgeInsets lehnt negative Werte ab. Um ein Kind aus seiner Box hinauszuschieben, nehmen Sie Transform.translate oder OverflowBox.

Häufige Fehler

1. Padding quetscht das Kind in einer engen Box

Symptom: Sie haben in einer Box mit fester Breite Padding ergänzt, und der Text bricht um oder das Icon schrumpft. Padding vergrößert nie seine eigene Größe; es zieht den Abstand vom Erhaltenen ab und reicht dem Kind den Rest. In einer 100 Pixel breiten SizedBox lässt EdgeInsets.all(20) dem Kind 60 Pixel.

// Das Kind bekommt 60 Pixel Breite
SizedBox(
  width: 100,
  child: Padding(
    padding: const EdgeInsets.all(20),
    child: Text('Ein langer Text bricht hier um'),
  ),
)

Lösung: Die Box um den Padding-Betrag vergrößern oder das Padding außerhalb der Box platzieren.

2. Langen Text in einer Row mit Padding versehen

Symptom: A RenderFlex overflowed by 42 pixels on the right. Row gibt seinen Kindern unbegrenzte Breite; Padding reicht das an den Text weiter, der auf einer Zeile bleibt und überläuft.

Row(
  children: [
    const Icon(Icons.info),
    Expanded(
      child: Padding(
        padding: const EdgeInsets.only(left: 8),
        child: Text('Dieser Text bricht jetzt im Restplatz um'),
      ),
    ),
  ],
)

Das Padding in ein Expanded zu setzen löst das Problem; warum Expanded dort nötig ist, erkläre ich im Beitrag zu Expanded.

3. Einen negativen Wert übergeben

Symptom: Die App stürzt im Debug-Modus mit der Assertion 'padding.isNonNegative': is not true ab. EdgeInsets darf nicht negativ sein; wenn Sie das Kind verschieben möchten, nehmen Sie Transform.translate(offset: Offset(-10, 0)).

4. Eine ListView in SafeArea einpacken

Symptom: Beim Scrollen gleiten die Elemente nicht natürlich unter die Statusleiste; oben bleibt eine harte Lücke. Ohne explizites padding wendet ListView die System-Insets entlang der Scrollachse bereits selbst an; ein außen herumgelegtes SafeArea verkleinert die Liste nur. Lösung: SafeArea entfernen und padding entweder weglassen oder einen eigenen Wert wie ListView(padding: MediaQuery.paddingOf(context)) übergeben. Die Elemente gleiten beim Scrollen unter die Notch, das erste Element beginnt trotzdem im sicheren Bereich.

Mini-Szenario: Login-Formular

Stellen Sie sich einen Anmeldebildschirm vor: Inhalt 24 Pixel vom Bildschirmrand entfernt, 16 Pixel zwischen den Feldern, und der Button darf beim Scrollen nicht an der Tastatur kleben.

class LoginScreen extends StatelessWidget {
  const LoginScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Text('Anmelden', style: Theme.of(context).textTheme.headlineMedium),
              const SizedBox(height: 24),
              const TextField(
                decoration: InputDecoration(
                  labelText: 'E-Mail',
                  contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14),
                  border: OutlineInputBorder(),
                ),
              ),
              const SizedBox(height: 16),
              const TextField(
                obscureText: true,
                decoration: InputDecoration(
                  labelText: 'Passwort',
                  border: OutlineInputBorder(),
                ),
              ),
              const SizedBox(height: 24),
              FilledButton(
                style: FilledButton.styleFrom(
                  padding: const EdgeInsets.symmetric(vertical: 16),
                ),
                onPressed: () {},
                child: const Text('Anmelden'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Hier arbeiten drei Abstandswerkzeuge zusammen: SingleChildScrollView.padding legt die Seitenränder fest und gehört zum Scrollbereich; SizedBox liefert den Abstand zwischen den Feldern; contentPadding und FilledButton.styleFrom(padding:) sind der eigene Innenabstand der Komponenten und vergrößern ihre Tap-Fläche. SafeArea kümmert sich um die Notch, das Scaffold um die Tastatur. Die Validierungsseite dieses Formulars beschreibe ich im Beitrag zum Form-Widget.

Häufig gestellte Fragen

Was ist der Unterschied zwischen Padding und Margin?

Padding ist Innenabstand: Der Hintergrund des Widgets deckt die Lücke mit ab. Margin ist Außenabstand: Der Hintergrund beginnt erst nach der Lücke. Flutter hat kein eigenes Margin-Widget; der Parameter margin des Container ist in Wirklichkeit ein außen herumgelegtes Padding.

Ist padding im Container oder das Padding-Widget schneller?

Beide leisten dasselbe; erhält der Container ein padding, erzeugt er intern ein Padding-Widget. Wenn Sie nur Abstand brauchen, erzeugt Padding weniger Widgets; steht der Container ohnehin für Farbe oder Rahmen da, nutzen Sie dessen Parameter padding.

Warum akzeptiert EdgeInsets keine negativen Werte?

Padding reicht dem Kind den verbleibenden Raum weiter; ein negativer Wert hieße, dem Kind mehr Raum zu geben, als es selbst erhalten hat, was dem Layoutmodell widerspricht. Verwenden Sie Transform.translate, um ein Kind nach außen zu verschieben, und OverflowBox, um es überlaufen zu lassen.

Verwenden Sie die Parameter start/end von EdgeInsetsDirectional statt left/right. Das Widget wählt die Anfangskante automatisch anhand der nächsten Directionality; nur bei symmetrischem horizontalem Abstand macht es keinen Unterschied.

Kommentare