Snackbars in Jetpack Compose
Lerne, wie du mit Snackbars kurzes, nicht blockierendes Feedback und Aktionen in deinen Android-Apps anzeigst.
Wenn eine App eine Aktion erfolgreich abgeschlossen hat oder ein kleinerer Fehler aufgetreten ist, möchtest du den Nutzer darüber informieren, ohne ihn in seiner aktuellen Aufgabe zu unterbrechen. Hier kommen Snackbars ins Spiel. Sie blenden sich dezent am unteren Bildschirmrand ein, liefern eine kurze Information oder eine optionale Aktion und verschwinden nach wenigen Sekunden von selbst wieder. In der modernen Android-Entwicklung mit Jetpack Compose sind sie ein Standardwerkzeug für transientes Feedback.
Was ist das?
Eine Snackbar ist ein UI-Element aus dem Material Design, das eine kurze Nachricht über App-Prozesse anzeigt. Im Gegensatz zu Dialogen, die den gesamten Bildschirm abdunkeln und eine explizite Entscheidung des Nutzers erzwingen, sind Snackbars nicht blockierend. Der Nutzer kann die App weiterhin normal bedienen, während die Snackbar sichtbar ist.
Oft werden sie eingesetzt, um Bestätigungen anzuzeigen, beispielsweise wenn eine E-Mail versendet oder ein Element zu einer Liste hinzugefügt wurde. Sie können auch für Fehlerhinweise genutzt werden, die nicht kritisch sind, wie ein kurzzeitiger Verbindungsabbruch. Ein besonderes Merkmal ist die Möglichkeit, eine einzelne Aktion anzubieten. Das klassische Beispiel hierfür ist die “Rückgängig”-Aktion, nachdem ein Element gelöscht wurde. Historisch gesehen haben Snackbars in Android den älteren “Toast” weitgehend abgelöst, da sie interaktiv sind und sich besser in das moderne Layout-System integrieren lassen.
Wie funktioniert es?
In Jetpack Compose ist die Anzeige einer Snackbar eng mit dem State-Management und Coroutines verzahnt. Du rufst nicht einfach eine statische Funktion auf, sondern veränderst den Zustand eines Hosts, der wiederum die UI aktualisiert. Die wichtigsten Komponenten dafür sind der SnackbarHostState und der SnackbarHost.
Der SnackbarHostState verwaltet die Warteschlange der anzuzeigenden Nachrichten. Er stellt die Funktion showSnackbar() zur Verfügung. Da das Anzeigen einer Snackbar Zeit in Anspruch nimmt (sie ist sichtbar, wartet und verschwindet wieder), ist showSnackbar() eine suspendierende Funktion (Suspend Function). Das bedeutet, sie muss zwingend innerhalb einer Coroutine aufgerufen werden.
Der SnackbarHost ist das tatsächliche UI-Element (Composable), das im Layout platziert wird und auf Änderungen des SnackbarHostState reagiert. In den meisten Fällen nutzt du dafür ein Scaffold. Das Scaffold ist ein Layout-Grundgerüst, das bereits einen Slot für den SnackbarHost bietet und sicherstellt, dass die Snackbar an der richtigen Position, typischerweise oberhalb einer Bottom Navigation Bar, gezeichnet wird.
Wenn du showSnackbar() aufrufst, pausiert die Coroutine so lange, bis die Snackbar entweder vom System nach einem Timeout ausgeblendet wird, der Nutzer sie wegwischt oder die integrierte Aktion antippt. Die Funktion gibt dann ein SnackbarResult zurück, das du auswerten kannst, um auf die Aktion des Nutzers zu reagieren.
In der Praxis
Um eine Snackbar in Compose korrekt einzubinden, deklarierst du zunächst den SnackbarHostState und einen CoroutineScope. Der Scope wird benötigt, um die suspendierende Anzeige-Funktion aus einem regulären Klick-Event heraus zu starten.
Hier ist ein typisches Setup:
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import kotlinx.coroutines.launch
@Composable
fun SnackbarExampleScreen() {
val snackbarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()
Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
) { paddingValues ->
Button(
onClick = {
coroutineScope.launch {
val result = snackbarHostState.showSnackbar(
message = "Eintrag erfolgreich gelöscht.",
actionLabel = "Rückgängig",
duration = SnackbarDuration.Short
)
when (result) {
SnackbarResult.ActionPerformed -> {
// Hier die Logik zum Wiederherstellen des Eintrags
println("Aktion 'Rückgängig' wurde geklickt.")
}
SnackbarResult.Dismissed -> {
// Die Snackbar ist regulär verschwunden
println("Snackbar wurde ausgeblendet.")
}
}
}
},
modifier = Modifier.padding(paddingValues)
) {
Text("Eintrag löschen")
}
}
}
Eine häufige Stolperfalle in der Praxis ist der Lebenszyklus des CoroutineScope. Wenn du die Snackbar auslöst und der Nutzer im selben Moment den Bildschirm verlässt (wodurch das Composable aus dem Composition-Tree entfernt wird), wird der rememberCoroutineScope() abgebrochen. Das bedeutet, dass die Snackbar eventuell gar nicht mehr erscheint. Wenn die Nachricht unabhängig vom Bildschirm-Lebenszyklus wichtig ist, musst du den SnackbarHostState und das Event-Handling weiter oben in deiner Architektur ansiedeln, beispielsweise in einem globalen App-Status oder über den ViewModel-Scope in Kombination mit einem sauberen Event-Flow.
Ein weiterer Fehler ist das harte Reinkodieren von Fehlermeldungen direkt im UI-Code. In echten Projekten solltest du String-Ressourcen nutzen und Fehlerzustände aus dem ViewModel an die View weiterreichen, anstatt die Logik, wann welche Snackbar erscheint, komplett im Button-Click zu verstecken.
Fazit
Snackbars sind ein essenzielles Werkzeug, um Statusänderungen oder Fehler elegant an den Nutzer zu kommunizieren, ohne den Workflow zu stören. Die Verknüpfung mit dem Scaffold und Coroutines in Jetpack Compose erfordert anfänglich ein Umdenken, bietet aber eine saubere Kontrolle über den UI-Zustand. Überprüfe in deinem aktuellen Projekt, ob du Fehlermeldungen oder Bestätigungen anzeigen musst. Implementiere einen SnackbarHostState, provoziere einen Fehler in deiner Logik und nutze den Debugger, um den Ablauf vom Auslösen der Coroutine bis zur Auswertung des SnackbarResult Schritt für Schritt nachzuvollziehen. Teste dabei gezielt, was passiert, wenn du während der Anzeige mehrfach auf den Auslöser klickst.