Skip to Content

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 Listen
  • LazyVerticalGrid und LazyHorizontalGrid 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.