Lektion 11 - Flutter Widgets im Detail
Container, Expanded, Flexible und mehr sicher einsetzen
Nach der Widget-Einfuehrung geht es jetzt tiefer: Du lernst wichtige Flutter-Widgets
kennen, die du fast in jeder App brauchst. Der Fokus liegt darauf, wann du welches
Widget benutzt und welche Fehler dabei typisch sind.
1. Schnelluebersicht
Widget/KonzeptWofuer?
ContainerBox fuer Groesse, Farbe, Rahmen, Margin und Padding
ExpandedNimmt freien Platz in Row oder Column ein
FlexibleDarf freien Platz nutzen, muss ihn aber nicht komplett fuellen
SizedBoxFester Abstand oder feste Breite/Hoehe
PaddingInnenabstand um ein Kind-Widget
LayoutBuilderReagiert auf verfuegbaren Platz
FutureBuilderZeigt UI passend zu asynchronen Daten
TextStyleGestaltet Text: Groesse, Farbe, Gewicht, Abstand
2. Container
Container ist eine flexible Box. Du nutzt ihn, wenn ein Bereich eine Groesse,
Hintergrundfarbe, Umrandung, Rundung oder Abstand bekommen soll.
Container(
width: 180,
height: 80,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(12),
),
child: const Text('Info Box'),
)
width / heightLegt feste Groesse fest, wenn das Elternlayout das erlaubt.
paddingInnenabstand zwischen Rand und Inhalt.
decorationFarbe, Rahmen, Rundung oder Schatten.
childDas Widget, das im Container liegt.
Typischer Fehler: color und decoration gleichzeitig direkt am
Container setzen. Wenn du decoration nutzt, gehoert die Farbe dort hinein.
3. Padding
Padding gibt einem Widget Innenabstand. Es ist klarer als ein leerer
Container, wenn du wirklich nur Abstand brauchst.
Padding(
padding: const EdgeInsets.all(16),
child: Text('Mit Abstand'),
)
Nutze Padding, wenn du keinen Rahmen, keine Farbe und keine feste Box brauchst.
4. SizedBox
SizedBox ist perfekt fuer feste Abstaende oder feste Groessen. Es ist sehr
lesbar und wird haeufig zwischen Widgets eingesetzt.
Column(
children: [
Text('Oben'),
SizedBox(height: 16),
Text('Unten'),
],
)
Nutze SizedBox fuer einfachen Abstand statt komplizierter Leer-Widgets.
5. Expanded
Expanded funktioniert nur innerhalb von Row, Column oder
aehnlichen Flex-Layouts. Es sagt: Dieses Kind soll den freien Platz ausfuellen.
Row(
children: [
Expanded(
child: Container(color: Colors.blue),
),
Expanded(
child: Container(color: Colors.green),
),
],
)
In diesem Beispiel teilen sich beide Container den freien Platz gleichmaessig.
Mit flex kannst du das Verhaeltnis veraendern.
Row(
children: [
Expanded(flex: 2, child: Text('Breiter')),
Expanded(flex: 1, child: Text('Schmaler')),
],
)
Typischer Fehler: Expanded ausserhalb von Row oder Column
verwenden. Dann meldet Flutter einen Parent-Data-Fehler.
6. Flexible
Flexible ist verwandt mit Expanded. Der Unterschied:
Expanded zwingt das Kind, freien Platz zu fuellen. Flexible erlaubt
dem Kind, flexibler mit dem Platz umzugehen.
Expanded
Expanded(
child: Text('Fuellt Platz'),
)
Flexible
Flexible(
child: Text('Darf kleiner bleiben'),
)
Faustregel: Nutze Expanded, wenn der Platz wirklich gefuellt werden soll.
Nutze Flexible, wenn Inhalt natuerlicher wachsen oder kleiner bleiben darf.
7. TextStyle
TextStyle gestaltet Text. Du kannst Farbe, Groesse, Schriftgewicht,
Zeilenhoehe, Ausrichtung und vieles mehr beeinflussen.
Text(
'Flutter lernen',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.blue,
height: 1.3,
),
)
PropertyBedeutung
fontSizeTextgroesse
fontWeightSchriftgewicht, zum Beispiel FontWeight.bold
colorTextfarbe
heightZeilenhoehe als Faktor
letterSpacingAbstand zwischen Buchstaben
Wiederholte Textstile solltest du spaeter nicht ueberall kopieren, sondern in ein Theme
oder eigene Konstanten auslagern.
8. LayoutBuilder
LayoutBuilder gibt dir Informationen ueber den Platz, den ein Widget gerade
bekommt. Damit kannst du Layouts bauen, die auf kleine oder grosse Bereiche reagieren.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return Column(children: [
Text('Kleines Layout'),
Text('Unter dem Titel'),
]);
}
return Row(children: [
Text('Grosses Layout'),
Text('Neben dem Titel'),
]);
},
)
constraintsInformationen ueber verfuegbare Breite und Hoehe.
maxWidthWichtiger Wert fuer responsive Entscheidungen.
builderFunktion, die je nach Platz ein Widget zurueckgibt.
Nutze LayoutBuilder, wenn das Layout vom verfuegbaren Platz abhaengt, nicht
nur von einer festen Bildschirmgroesse.
9. FutureBuilder
FutureBuilder ist fuer Daten, die erst spaeter ankommen. Zum Beispiel:
Datei laden, API anfragen oder eine kuenstliche Wartezeit. Er zeigt je nach Zustand
eine andere UI.
FutureBuilder<String>(
future: loadName(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('Fehler beim Laden');
}
return Text(snapshot.data ?? 'Kein Name');
},
)
futureDie spaeter fertige Aufgabe.
snapshotEnthaelt Zustand, Daten oder Fehler.
waitingDaten sind noch nicht da, zeige Ladeanzeige.
hasErrorBeim Laden ist ein Fehler passiert.
dataDie geladenen Daten, wenn alles geklappt hat.
Typischer Fehler: Direkt snapshot.data! nutzen, obwohl Daten noch fehlen
koennen. Erst Zustand und Fehler pruefen.
10. Layout-Planungsaufgabe
Aufgabe: Plane eine responsive Profilkarte. Auf kleinen Flaechen stehen Bild, Name,
Beschreibung und Button untereinander. Auf grossen Flaechen stehen Bild und Text
nebeneinander. Daten fuer den Namen kommen spaeter aus einem Future.
Starte das Laden des Namens
Wenn Daten noch laden:
Zeige Ladeanzeige
Wenn Fehler beim Laden:
Zeige Fehlermeldung
Wenn Name geladen:
Pruefe verfuegbare Breite
Wenn Breite kleiner als 600:
Zeige Profilbild oben
Zeige Name darunter
Zeige Beschreibung darunter
Zeige Button darunter
Sonst:
Zeige Profilbild links
Zeige Name, Beschreibung und Button rechts
FutureBuilderPlant Ladezustand, Fehlerzustand und fertige Daten.
LayoutBuilderEntscheidet zwischen kleinem und grossem Layout.
ColumnNutze sie fuer kleine Flaechen.
RowNutze sie fuer grosse Flaechen.
Padding/SizedBoxSorgen fuer lesbare Abstaende.
11. Kombiniertes Beispiel
Dieses Beispiel verbindet mehrere der heutigen Konzepte. Lies zuerst die Struktur,
dann erst die Details.
FutureBuilder<String>(
future: loadName(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.hasError) {
return const Text('Fehler');
}
final name = snapshot.data ?? 'Gast';
return LayoutBuilder(
builder: (context, constraints) {
final content = Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Text(
name,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
);
if (constraints.maxWidth < 600) {
return Column(children: [content]);
}
return Row(children: [
Expanded(child: content),
const SizedBox(width: 16),
Flexible(child: Text('Profilbeschreibung')),
]);
},
);
},
)
12. Uebungen
- Baue drei farbige
Container in einer Row.
- Nutze
Expanded, damit alle drei gleich breit werden.
- Aendere einen Bereich auf
flex: 2 und beschreibe, was passiert.
- Ersetze ein
Expanded durch Flexible und vergleiche das Verhalten.
- Erstelle einen Titel mit
TextStyle.
- Nutze
LayoutBuilder, um bei kleiner Breite eine Column und bei grosser Breite eine Row zu zeigen.
- Baue einen einfachen
FutureBuilder, der nach kurzer Zeit Text anzeigt.
- Schreibe fuer jede Aufgabe zuerst eine kurze Layout-Planung.
Weiter zum Flutter Framework-Update