Skip to Content

Zustand im UI

Dauer: 35 Minuten

  • state mit mutableStateOf und remember

Ziel: Zustand in Compose kennenlernen

Zustand ist ein zentrales Konzept in der Entwicklung von Benutzeroberflächen und findet sich in vielen modernen UI-Frameworks (Compose in Android, Flutter, React Native).

In Compose wird der Zustand direkt in Composables oft als Wert einer Variablen gespeichert. Wenn sich der Zustand bzw. der Wert der Variablen ändert, wird das Composable automatisch neu im UI dargestellt.

In unserer App kann jedes Todo in einem von zwei Zuständen sein:

  • Erledigt (Checkbox ist markiert)
  • Nicht erledigt (Checkbox ist nicht gesetzt)

Wir werden dies mit einer state-Variablen done für jedes Todo umsetzen.

state-Variable für Todo

Es gibt zwei Funktionen, die für die Verwaltung von Zustand in Compose verwendet werden:

  • mutableStateOf (für den Zustand, der sich ändern kann)
  • remember (für den Zustand, der überleben soll, wenn das Composable neu gerendert wird)

In unserem Fall verwenden wir mutableStateOf für den Zustand, ob ein Todo erledigt ist oder nicht. Dazu erstellen wir eine mutableStateOf-Variable vom Typ Boolean mit dem initialen Wert false. Zusätzlich verwenden wir remember, damit sich das Composable an den Zustand nach dem erneuten Darstellen „erinnert“:

val done = remember { mutableStateOf(false) }

Mit val deklarieren wir eine unveränderliche Referenz. done ist nun ein MutableState<Boolean>, der den Zustand des Todos speichert. Wir können den Wert von done mit done.value lesen und setzen. Hier der Code des Todo-Composables mit Zustand:

@Composable fun Todo(text: String) { val done = remember { mutableStateOf(false) } Row( modifier = Modifier .clickable(onClick = {}) .padding(10.dp) .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { Checkbox(checked = done.value, onCheckedChange = { newVal -> done.value = newVal }) Text(text, fontWeight = FontWeight.Bold, fontSize = 22.sp) } }

Der Zustand des Todos wird in done gespeichert und in der Checkbox verwendet. Wenn wir die Checkbox anklicken, dann wird der Wert von done aktualisiert. Die Checkbox zeigt durch checked = done.value den aktuellen Wert von done an.

Wenn sich der Zustand bzw. state ändert, wird das Composable automatisch neu dargestellt — dies wird Recomposition genannt.

Es hat sich die Sichtweise bewährt, dass das UI eine Funktion von State ist:

UI = f(State)

Die Funktionsweise von State in Compose erinnert somit stark an das State-Konzept in React (Native) — und umgekehrt.

Bemerkungen zu Kotlin

Es folgen ein paar kurze Bemerkung zu Kotlin im Umgang mit Compose. Oftmals gibt es in Kotlin mehrere Wege, um das gleiche Ziel zu erreichen, und sogenannter „idiomatischer“ Code wird bevorzugt (speziell in Compose).

Folgende Tipps helfen beim Umgang mit Kotlin.

KI-Tools wie ChatGPT oder Gemini können Kotlin-Code sehr gut erklären.

Im Android-Einsteigerkurs von Google gibt es einige Abschnitte zu den wichtigsten Kotlin-Konzepten für die Android-Entwicklung: https://developer.android.com/courses/android-basics-compose/course 

Mit val deklarieren wir zwar eine unveränderliche Referenzvariable (val done…), aber durch mutableStateOf können wir den Wert bzw. „Inhalt“ der state-Variablen ändern. Die Variable done selbst ist daher unveränderlich, aber ihr Inhalt kann sich ändern.

In der Lambda-Funktion zu onCheckedChange in der Checkbox haben wir nur ein Argument newVal und setzen den Wert von done auf newVal:

onCheckedChange = { newVal -> done.value = newVal }

Dies lässt sich in einer kürzeren Schreibweise ausdrücken:

onCheckedChange = { done.value = it }

Erklärung: it ist ein implizites Argument, das in Lambda-Funktionen verwendet wird, wenn nur ein Argument übergeben wird.

Außerdem gibt es in Kotlin die Möglichkeit, mit by eine sogenannte Delegated Property zu verwenden (siehe https://kotlinlang.org/docs/delegated-properties.html ). Diese wird in Compose oft für Zustand verwendet:

var done by remember { mutableStateOf(false) }

Zu beachten ist, dass val nun in var geändert wurde, da done sich nun ändern kann.

Damit by funktioniert, müssen neben remember und mutableStateOf folgende Imports hinzugefügt werden:

import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue

Nun können wir direkt mit done arbeiten, ohne auf done.value zugreifen zu müssen.

Hier der komplette, idiomatische Code des Todo-Composables mit by und it:

@Composable fun Todo(text: String) { var done by remember { mutableStateOf(false) } Row( modifier = Modifier .clickable(onClick = {}) .padding(10.dp) .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { Checkbox(checked = done, onCheckedChange = { done = it }) Text(text, fontWeight = FontWeight.Bold, fontSize = 22.sp) } }

Es ist auch möglich, in Compose folgendermaßen mit Zustand umzugehen:

val (done, setDone) = remember { mutableStateOf(false) } // Row usw. hier weggelassen… Checkbox(checked = done, onCheckedChange = { setDone(it) })

Hier wird ein done-Zustand mit mutableStateOf erstellt und in der Checkbox verwendet. setDone ist eine Funktion, um den Zustand zu ändern.

⟶ Diese Schreibweise erinnert stark an den Umgang mit Zustand in React (Native):

// State in React (Native) mit useState (JavaScript) const [done, setDone] = useState(false);

Zustandsänderung durch Antippen der Row

Durch eine kleine Änderung erreichen wir, dass wir den Zustand des Todos nicht nur durch Klicken auf die Checkbox, sondern auch durch Klicken auf die ganze Zeile ändern können. Wir müssen dazu nur die Lambda-Funktion in clickable wie folgt anpassen:

Row( modifier = Modifier .clickable(onClick = { done = !done }) // Rest weggelassen… )

Durch einen Code Review mit ChatGPT wurde noch diese Änderung vorgeschlagen:

Row( modifier = Modifier .toggleable(value = done, onValueChange = { done = it }, role = Role.Checkbox) .padding(10.dp) .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { Checkbox(checked = done, onCheckedChange = null) // onCheckedChange null wegen toggleable oben Text(text, fontWeight = FontWeight.Bold, fontSize = 22.sp) }

Mit role = Role.Checkbox bekommt die ganze Zeile die Semantik „Checkbox“ inkl. Zustand (an/aus). Dies verbessert die Zugänglichkeit (Accessibility bzw. A11y) der App. Die Row ist nun die einzige Quelle für onValueChange.

KI-Tools wie ChatGPT, GitHub Copilot oder Gemini sind hilfreich, um Code Reviews durchzuführen. Dadurch verbessern wir die Code-Qualität und lernen gelegentlich neue Techniken und Best Practices dazu.

Weiterführendes Material

Neben remember gibt es auch rememberSaveable, das den Zustand auch über Prozess-Neustarts bzw. Konfigurationswechsel hinweg speichert (z.B. bei Drehung des Geräts). Dazu und allgemein zu State in Compose findet sich mehr unter https://developer.android.com/develop/ui/compose/state .

Thinking in Compose („mentales Modell“): https://developer.android.com/develop/ui/compose/mental-model 

Im Android Developer YouTube Channel findet sich umfangreiches Material von Google: https://www.youtube.com/@AndroidDevelopers/videos