Zustand und Listen
Dauer: 25 Minuten
- Listen mit
listOf
in Kotlin LazyColumn
für Listen in Compose- Todos der Liste hinzufügen
Ziel: Einstieg in den Umgang mit Listen
Listen mit listOf
in Kotlin
Unsere drei Beispiel-Todos im TodoList-Composable könnten wir
auch in einer Liste speichern. In Kotlin wird mit listOf
eine
Liste erstellt und wir können die Elemente mit einer Schleife
durchgehen:
@Composable
fun TodoList(modifier: Modifier = Modifier) {
val todos = listOf("Einkaufen", "Android lernen", "Sport machen")
Column(modifier) {
for (todo in todos) {
Todo(todo)
}
}
}
Kotlins Standardbibliothek beschreibt die vielseitigen Listen bzw. Collections im Detail: https://kotlinlang.org/docs/collections-overview.html
Zustand der Liste
Wir bereiten nun die Verwaltung von Todos in einer Liste vor. Dazu verschieben
wir die Liste der Todos in den Zustand der MainActivity
und verwalten diese
als Zustand, damit wir später neue Todos hinzufügen können.
Das „Hochziehen“ des Zustands in der Composable-Hierarchie wird state hoisting genannt und ist nötig, damit der FAB für das Hinzufügen neuer Todos Zugriff auf den Zustand hat.
setContent {
// Zustand der Todos ist nun in MainActivity
val todos = remember { mutableStateListOf("Einkaufen", "Android lernen", "Sport machen") }
TodosAndroidTheme {
Scaffold(floatingActionButton = {
FloatingActionButton(onClick = {},
content = { Icon(Icons.Filled.Add, contentDescription = "Todo erstellen") })
}) { innerPadding ->
// Zustand (todos) wird an TodoList übergeben
TodoList(todos = todos, modifier = Modifier.padding(innerPadding))
}
}
}
@Composable
fun TodoList(todos: List<String>, modifier: Modifier = Modifier) {
// Liste der Todos wird nun als Parameter übergeben
Column(modifier) {
for (todo in todos) {
Todo(todo)
}
}
}
LazyColumn
für Listen in Compose
LazyColumn
ist ein Composable, das eine scrollbare Liste von
Elementen darstellt.
Wenn wir längere Listen von Elementen zur Darstellung haben, dann ist
Column
ineffizient, da alle Elemente sofort gerendert werden.
LazyColumn
ist effizienter als Column
. Außerdem wird die Liste
durch die Verwendung von LazyColumn
automatisch scrollbar und optimiert:
@Composable
fun TodoList(todos: List<String>, modifier: Modifier = Modifier) {
LazyColumn(modifier) {
items(todos) { todo ->
Todo(todo)
}
}
}
Wenn für diesen Code-Abschnitt unerwartete Compiler-Fehler auftauchen, dann lassen
sie sich wie fast immer durch Alt-Enter
beheben. Hier wird neben LazyColumn
ein Import für items
benötigt:
import androidx.compose.foundation.lazy.items
items
ist eine Extension-Funktion von LazyColumn
, siehe
https://developer.android.com/develop/ui/compose/lists#lazylistscope
Es gibt weitere Möglichkeiten, Listen in Compose zu erstellen:
LazyRow
für horizontale ListenLazyVerticalGrid
undLazyHorizontalGrid
für Grids (Kacheln)
Siehe Guide zu Lists: https://developer.android.com/develop/ui/compose/lists
Todos der Liste hinzufügen
Wenn der FAB (Floating Action Button) gedrückt wird, soll ein neues Todo erzeugt werden.
Wir können hier mit ChatGPT den Code für einen Dialog generieren lassen und mit dem unten folgenden Beispiel vergleichen, um zu zeigen, dass die KI-Tools einerseits nützlich und andererseits nicht-deterministisch sind.
Der folgende Code wurde mit ChatGPT generiert.
Prompt:
Ich habe eine Android Compose App, wobei der komplette Code bisher
in der MainActivity enthalten (siehe unten). Ich möchte mit dem FAB
ein Todo hinzufügen können. Generiere mir bitte den Code für einen
Dialog, in dem ich den Text für das neue Todo eingeben kann.
// Code aus MainActivity einfügen…
Wir erstellen im package ui
eine neue Datei AddTodoDialog.kt
mit
dem von ChatGPT generierten Code:
// ggf. package anpassen!
package com.example.todosandroid2025.ui
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
@Composable
fun AddTodoDialog(
onAdd: (String) -> Unit,
onDismiss: () -> Unit
) {
var text by remember { mutableStateOf("") }
AlertDialog(
onDismissRequest = onDismiss,
title = { Text("Neues Todo") },
text = {
OutlinedTextField(
value = text,
onValueChange = { text = it },
placeholder = { Text("Was möchtest du tun?") },
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
},
confirmButton = {
TextButton(
onClick = { if (text.isNotBlank()) onAdd(text.trim()) },
enabled = text.isNotBlank()
) { Text("Hinzufügen") }
},
dismissButton = {
TextButton(onClick = onDismiss) { Text("Abbrechen") }
}
)
}
Wir sollten nun in der Lage sein, die Funktionsweise des obigen Codes grundlegend nachzuvollziehen (auch wenn dort andere Composables verwendet werden).
In MainActivity
fügen wir den Dialog hinzu und passen den FAB an
(Änderungen sind mit Kommentaren markiert):
setContent {
val todos = remember { mutableStateListOf("Einkaufen", "Android lernen", "Sport machen") }
var showDialog by remember { mutableStateOf(false) }
TodosAndroid2025Theme {
Scaffold(floatingActionButton = {
FloatingActionButton(
onClick = { showDialog = true},
) {
Icon(
painter = painterResource(R.drawable.add_24px),
contentDescription = "Neues Todo erstellen"
)
}
}) { innerPadding ->
if (showDialog) {
AddTodoDialog(
onAdd = { text ->
todos.add(text)
showDialog = false
},
onDismiss = { showDialog = false }
)
}
TodoList(todos = todos, modifier = Modifier.padding(innerPadding))
}
}
}
Nun sind wir in der Lage, neue Todos einzugeben und der Liste hinzuzufügen.
Aus der MainActivity
übergeben wir dem Dialog eine Lambda-Funktion
onAdd
, die den Text des neuen Todos entgegennimmt und das Todo der
Liste hinzufügt. Dadurch ändert sich der Zustand der Liste und die UI
wird automatisch aktualisiert (Recomposition).
Dieses Vorgehen ähnelt dem bekannten Callback-Pattern in JavaScript.