Zustand im UI
Dauer: 35 Minuten
- state mit
mutableStateOf
undremember
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.
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