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

Einführung in Flutter: Widget-Baum, Row, Column und Stack

Ahmet Balaman

Zuletzt aktualisiert:

5 Min. Lesezeit

FlutterWidgetRowColumnStackUI Design
Einführung in Flutter: Widget-Baum, Row, Column und Stack

Heute ist ein historischer Tag! Nachdem ich die Dart-Grundlagen gelernt habe, bin ich endlich zu Flutter gewechselt. Die Installationsschritte finden Sie im Leitfaden zu den ersten Schritten mit Flutter; in diesem Beitrag steige ich direkt in den Code ein. Mein erster Eindruck: Widgets, Widgets, überall Widgets!

Live-Demo: Beispiele zu Row, Column und Stack

Probieren Sie die grundlegenden Layout-Strukturen von Flutter interaktiv aus:

💡 Falls das Beispiel oben nicht lädt, klicken Sie auf DartPad, um es in einem neuen Tab auszuführen.

Den Aufbau von Flutter verstehen

Das Erste, was mir in einem Flutter-Projekt auffiel, war dies: Alles ist wie eine Baumstruktur. Es beginnt mit der main-Funktion, dann das App-Widget, dann die Seite, dann das Scaffold … eine verschachtelte Struktur.

Grundaufbau einer Flutter-App

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp()); // App starts here
}

Die Funktion main() ist der Ausgangspunkt von allem. Mit runApp() starten wir unsere App.

Das Haupt-Widget (MyApp)

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'First App',
      debugShowCheckedModeBanner: false, // Remove debug banner
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'My First Project'),
    );
  }
}

MaterialApp erlaubt es, Apps in Flutter nach den Regeln des Material Design zu bauen. Der Parameter home legt die erste Seite fest, die geöffnet wird.

Das Seiten-Widget (MyHomePage)

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

StatefulWidget ist für Widgets gedacht, deren Zustand sich ändern kann. Wenn Sie zum Beispiel einen Zähler mit Button bauen, muss sich die Anzeige bei jeder Änderung aktualisieren. Genau dafür ist StatefulWidget da.

Die State-Klasse

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    print("Page loaded!");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Text("Hello Flutter!"),
      ),
    );
  }
}

initState() läuft einmal beim ersten Öffnen der Seite. Hier werden üblicherweise API-Aufrufe untergebracht. In welcher Reihenfolge initState, build und dispose laufen, erkläre ich im Beitrag zum Flutter-Lebenszyklus.

Scaffold ist das Grundgerüst einer Seite. Es enthält Bereiche wie AppBar (obere Leiste), body (Inhalt) und bottomNavigationBar.

Die Baumstruktur von Flutter

Der Schlüssel zum Verständnis von Flutter ist diese Baumstruktur:

Ein Flutter-Widget-Baum von MaterialApp über Scaffold zu AppBar und Column und von Column zu Text und Row

Main
 └─ MyApp (MaterialApp)
     └─ MyHomePage (Scaffold)
         ├─ AppBar
         └─ Body
             └─ Widgets...

Alles ist ineinander verschachtelt. Stellen Sie es sich so vor: Es gibt ein Gebäude (MaterialApp), das Gebäude hat Räume (Scaffold), und in den Räumen stehen Möbel (Widgets).

Layout gestalten: Row, Column und Stack

Für das Layout gibt es in Flutter drei grundlegende Strukturen. Anfangs war ich unsicher, wann ich welche einsetzen soll.

Row – horizontale Anordnung

Row ordnet Widgets nebeneinander an, von links nach rechts.

Row(
  children: [
    Container(width: 80, height: 80, color: Colors.blue),
    Container(width: 80, height: 80, color: Colors.red),
    Container(width: 80, height: 80, color: Colors.yellow),
  ],
)

Ausgabe auf dem Bildschirm: 🟦🟥🟨 (drei Quadrate nebeneinander)

Column – vertikale Anordnung

Column ordnet Widgets untereinander an, von oben nach unten.

Column(
  children: [
    Container(width: 80, height: 80, color: Colors.blue),
    Container(width: 80, height: 80, color: Colors.red),
    Container(width: 80, height: 80, color: Colors.yellow),
  ],
)

Ausgabe auf dem Bildschirm:

🟦
🟥
🟨

(drei Quadrate untereinander)

Stack – überlappende Anordnung

Stack legt Widgets übereinander, wie Ebenen.

Stack(
  children: [
    Container(width: 130, height: 100, color: Colors.blue),
    Container(width: 80, height: 70, color: Colors.red),
    Container(width: 30, height: 60, color: Colors.yellow),
  ],
)

Unten die große blaue Box, in der Mitte die mittlere rote, oben die kleine gelbe. Alle sind an der oberen linken Ecke ausgerichtet.

Row und Column kombinieren

Nun bauen wir ein etwas komplexeres Layout und kombinieren Zeilen und Spalten:

Ziel: 2 Zeilen mit je 4 Spalten (insgesamt 8 Kästchen)

Column(
  children: [
    // First row
    Row(
      children: [
        Container(width: 80, height: 80, color: Colors.yellow),
        Container(width: 80, height: 80, color: Colors.red),
        Container(width: 80, height: 80, color: Colors.green),
        Container(width: 80, height: 80, color: Colors.blue),
      ],
    ),
    // Second row
    Row(
      children: [
        Container(width: 80, height: 80, color: Colors.red),
        Container(width: 80, height: 80, color: Colors.blue),
        Container(width: 80, height: 80, color: Colors.yellow),
        Container(width: 80, height: 80, color: Colors.green),
      ],
    ),
  ],
)

Ergebnis:

🟨 🟥 🟩 🟦
🟥 🟦 🟨 🟩

Die Logik ist folgende: Die Hauptstruktur ist eine Column (vertikal). Darin liegen zwei Rows (horizontal). Jede Row enthält vier Container.

Das Container-Widget

Container ist eines der grundlegendsten Widgets in Flutter. Stellen Sie es sich als Kasten vor.

Einfacher Container

Container(
  width: 100,
  height: 100,
  color: Colors.blue,
  child: Text("Hello"),
)

Inhalt zentrieren

Container(
  width: 100,
  height: 100,
  color: Colors.blue,
  child: Center(
    child: Text(
      "Hello",
      style: TextStyle(color: Colors.white),
    ),
  ),
)

Das Widget Center zentriert das, was in ihm liegt; die Details stehen im Beitrag zum Center-Widget.

Margin und Padding

Container(
  width: 100,
  height: 100,
  margin: EdgeInsets.all(10),  // Outer spacing
  padding: EdgeInsets.all(20), // Inner spacing
  color: Colors.blue,
  child: Text("Hello"),
)
  • Margin: Abstand außerhalb des Containers
  • Padding: Abstand innerhalb des Containers

Brauchen Sie nur Innenabstand, ist das Padding-Widget anstelle eines Containers die leichtere Lösung.

Ecken abrunden

Container(
  width: 100,
  height: 100,
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(20),
  ),
  child: Center(
    child: Text("Rounded"),
  ),
)

Wenn Sie decoration verwenden, müssen Sie den Parameter color innerhalb von decoration angeben.

Rahmen hinzufügen

Container(
  width: 100,
  height: 100,
  decoration: BoxDecoration(
    color: Colors.white,
    border: Border.all(
      color: Colors.blue,
      width: 3,
    ),
  ),
  child: Center(
    child: Text("Bordered"),
  ),
)

Ausrichtung in Row und Column

Wir können Widgets dort ausrichten, wo wir sie haben möchten:

MainAxisAlignment

Row(
  mainAxisAlignment: MainAxisAlignment.center, // Center
  children: [
    Container(width: 50, height: 50, color: Colors.red),
    Container(width: 50, height: 50, color: Colors.blue),
  ],
)

Bei einer Row verläuft die Hauptachse horizontal. Die Optionen:

  • start: Links ausrichten
  • end: Rechts ausrichten
  • center: Zentrieren
  • spaceBetween: Gleicher Abstand dazwischen
  • spaceAround: Gleicher Abstand ringsum
  • spaceEvenly: Überall gleicher Abstand

CrossAxisAlignment

Row(
  crossAxisAlignment: CrossAxisAlignment.center, // Center vertically
  children: [
    Container(width: 50, height: 50, color: Colors.red),
    Container(width: 50, height: 100, color: Colors.blue),
  ],
)

Bei einer Row verläuft die Querachse vertikal. Die Optionen:

  • start: Oben ausrichten
  • end: Unten ausrichten
  • center: Zentrieren
  • stretch: Dehnen

Damit die Ausrichtung überhaupt wirkt, spielt auch eine Rolle, wie viel Platz die Row einnimmt; diesen Zusammenhang mit mainAxisSize behandle ich im Beitrag zu mainAxisSize und Ausrichtung.

Praxisbeispiel: Layout eines Taschenrechners

Column(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    // Screen
    Container(
      width: double.infinity,
      height: 100,
      color: Colors.grey[300],
      child: Center(
        child: Text(
          "0",
          style: TextStyle(fontSize: 48),
        ),
      ),
    ),
    // Buttons
    Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        createButton("7"),
        createButton("8"),
        createButton("9"),
        createButton("/"),
      ],
    ),
    Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        createButton("4"),
        createButton("5"),
        createButton("6"),
        createButton("*"),
      ],
    ),
  ],
)

Was ich am ersten Tag mitgenommen habe

  • In Flutter ist alles ein Widget
  • Row horizontal, Column vertikal, Stack überlappend
  • Container ist das Widget, das ich am häufigsten nutzen werde
  • Ausgerichtet wird mit MainAxisAlignment und CrossAxisAlignment
  • Scaffold ist das Grundgerüst einer Seite

Morgen schaue ich mir Buttons, State Management und dynamische Inhalte an. Diese Flutter-Reise ist wirklich spannend!

Nützliche Ressourcen


Möchten Sie Flutter mit mir lernen?

Bleiben Sie dran und lernen Sie Flutter weiter! 🚀

Häufig gestellte Fragen

Was passiert, wenn die Widgets in einer Row nicht auf den Bildschirm passen?

Sie sehen am Bildschirmrand einen gelb-schwarz gestreiften „Overflow“-Balken und in der Konsole die Warnung „A RenderFlex overflowed“. Die Lösung: Wickeln Sie die überlaufenden Kinder in Expanded oder Flexible, oder legen Sie die Row in eine SingleChildScrollView, wenn der Inhalt scrollen soll.

Wie entscheide ich zwischen StatelessWidget und StatefulWidget?

Zeigt das Widget nur die Parameter an, die es erhält, genügt ein StatelessWidget. Muss sich die Anzeige durch Nutzerinteraktion oder eintreffende Daten neu zeichnen (Zähler, Formular, Ladezustand), verwenden Sie ein StatefulWidget.

Muss ich für jeden Kasten einen Container verwenden?

Nein. Padding nur für Abstand, SizedBox nur für Größe und Center nur zum Zentrieren sind jeweils leichter. Greifen Sie zum Container, wenn Sie eine decoration wie Farbe, Rahmen oder abgerundete Ecken brauchen.

Kommentare