UI-State als sealed interface – Loading, Success, Error sauber modellieren
Zwei Booleans für "lädt" und "Fehler"? Das gibt unmögliche Zustände. Mit einem sealed interface bildest du genau die Fälle ab, die es wirklich gibt – und der Compiler hilft dir.
Viele Screens tragen Zustände wie isLoading, error und data als einzelne Felder mit sich herum. Das Problem: Damit sind unmögliche Kombinationen möglich – „lädt gerade" *und* „Fehler" *und* „Daten da" gleichzeitig. Mit einem sealed interface modellierst du stattdessen genau die Fälle, die es wirklich gibt.
Der Zustand als abgeschlossene Menge
sealed interface UiState {
data object Loading : UiState
data class Success(val user: User) : UiState
data class Error(val message: String) : UiState
}„Sealed" heißt: Alle möglichen Ausprägungen stehen in dieser Datei – niemand kann von außen einen vierten, unerwarteten Zustand hinzufügen. Genau das nutzt der Compiler gleich aus.
Im ViewModel
private val _state = MutableStateFlow<UiState>(UiState.Loading)
val state: StateFlow<UiState> = _state.asStateFlow()
fun load(id: String) {
viewModelScope.launch {
_state.value = UiState.Loading
_state.value = runCatching { repo.user(id) }
.fold(
onSuccess = { UiState.Success(it) },
onFailure = { UiState.Error(it.message ?: "Unbekannter Fehler") }
)
}
}In Compose – der Compiler zwingt dich zur Vollständigkeit
when (val s = state) {
UiState.Loading -> CircularProgressIndicator()
is UiState.Success -> UserCard(s.user) // s.user ist hier typsicher verfügbar
is UiState.Error -> ErrorBanner(s.message)
}Weil UiState sealed ist, ist dieses when erschöpfend – vergisst du einen Fall, gibt es einen Compile-Fehler statt eines Bugs zur Laufzeit. Und dank Smart-Cast greifst du in jedem Zweig typsicher auf genau die Daten zu, die es dort gibt (s.user, s.message).
Warum sich das lohnt
- Keine unmöglichen Zustände mehr – jeder Zustand ist genau einer der drei Fälle.
- Compiler als Sicherheitsnetz: Neue Fälle erzwingen, dass du alle
whenanpasst. - Lesbar: Der Typ dokumentiert selbst, was passieren kann.
Ich baue Android-Apps mit sauberer State-Architektur in Kotlin & Compose – sprich mich an.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
Google Gemini in einer Android-App (Kotlin, über dein eigenes Backend)
Ein KI-Feature in eine Kotlin-App bringen – mit Google Gemini. Warum der API-Key nie in die App gehört, wie der generateContent-Aufruf aussieht und wie du ihn sauber aus einer Coroutine startest.
withContext(Dispatchers.IO) – schwere Arbeit vom Main-Thread nehmen
Datei lesen oder ein Bild dekodieren blockiert den Main-Thread und ruckelt die UI. Mit withContext(Dispatchers.IO) schiebst du blockierende Arbeit sauber auf einen Hintergrund-Thread.
Navigation in Compose – NavHost mit Argumenten
Von der Liste zum Detail und mit einer ID zurück: Navigation Compose macht das mit NavHost, Routen und typisierten Argumenten. So richtest du den Grundfluss sauber ein.