Einführung in Flutter: Widget-Baum, Row, Column und Stack
Zuletzt aktualisiert:
5 Min. Lesezeit

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:
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 ausrichtenend: Rechts ausrichtencenter: ZentrierenspaceBetween: Gleicher Abstand dazwischenspaceAround: Gleicher Abstand ringsumspaceEvenly: Ü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 ausrichtenend: Unten ausrichtencenter: Zentrierenstretch: 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.
Verwandte Artikel
Flutter: Flexible Abstände mit dem Spacer-Widget
Was in Spacer steckt, Abstände mit flex gewichten, der Unterschied zu SizedBox und mainAxisAlignment und warum Spacer in scrollbaren Bereichen scheitert.
Flutter: Platz und Ausrichtung mit mainAxisSize steuern
max und min bei mainAxisSize, der Einfluss der Constraints, warum mainAxisAlignment scheinbar nichts tut, und Rezepte für Dialoge, Sheets und Button-Gruppen.
Flutter: Platz ausfüllen mit dem Expanded-Widget
Expanded im Detail: Wie der flex-Anteil berechnet wird, Verschachtelung, warum es nur in Row und Column funktioniert und die drei häufigsten Fehler.