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
Einstellungen speichern mit DataStore – der moderne Ersatz für SharedPreferences
SharedPreferences ist alt, blockiert den Main-Thread und kennt keine Coroutinen. DataStore macht dasselbe – nur asynchron, typsicher und als Flow. So speicherst du Nutzer-Einstellungen richtig.
Parallele Netzwerkaufrufe mit async/await – zwei Requests, halbe Wartezeit
Wenn ein Screen zwei unabhängige Daten braucht, musst du nicht erst auf den einen und dann auf den anderen Request warten. Mit async und coroutineScope laufen sie parallel – strukturiert und sicher.
DisposableEffect in Compose – aufräumen, wenn ein Composable verschwindet
Immer wenn du in Compose etwas registrierst – einen Listener, einen Sensor, einen Callback – musst du es auch wieder abmelden. Genau dafür gibt es DisposableEffect mit seinem onDispose-Block.