derivedStateOf – abgeleiteten Zustand berechnen, ohne Recompositions zu verschwenden
Wenn du aus einem sich schnell ändernden State einen selten wechselnden Wert ableitest (z. B. "Nach-oben-Button ab Item 5"), verhindert derivedStateOf, dass dein UI bei jedem Scroll-Pixel neu zeichnet.
Ein klassischer Compose-Performance-Fehler: Du liest einen State, der sich sehr oft ändert, um daraus einen Wert zu berechnen, der sich selten ändert. Beispiel: „Zeige den Nach-oben-Button, sobald der Nutzer über Item 5 hinausgescrollt hat." Naiv geschrieben zeichnet dein UI bei jedem Scroll-Frame neu – obwohl sich das Ergebnis fast nie ändert.
Das Problem
val listState = rememberLazyListState()
// ❌ liest firstVisibleItemIndex direkt → recomposed bei JEDEM Scroll-Tick
val showButton = listState.firstVisibleItemIndex > 5Jede Scroll-Bewegung ändert firstVisibleItemIndex, also wird der umgebende Composable ständig neu ausgewertet – auch wenn showButton konstant true bleibt.
Die Lösung: derivedStateOf
val showButton by remember {
derivedStateOf { listState.firstVisibleItemIndex > 5 }
}derivedStateOf merkt sich das Ergebnis und benachrichtigt Leser nur dann, wenn sich genau dieses Ergebnis (true/false) ändert – nicht bei jeder Änderung der Eingabe. Aus „recomposed bei jedem Pixel" wird „recomposed genau zweimal": beim Überschreiten und beim Zurückscrollen.
if (showButton) {
FloatingActionButton(onClick = { /* nach oben scrollen */ }) {
Icon(Icons.Default.KeyboardArrowUp, contentDescription = "Nach oben")
}
}Wann – und wann nicht
- Einsetzen, wenn aus einem häufig wechselnden State ein selten wechselnder Wert entsteht (Scroll-Position → Sichtbarkeit, langer Text → „ist gültig").
- Nicht nötig, wenn sich Eingabe und Ergebnis ähnlich oft ändern – dann ist es nur zusätzlicher Overhead. Für eine reine 1:1-Transformation reicht normales
remember(key). - Immer in
rememberwickeln, damit das abgeleitete State-Objekt über Recompositions erhalten bleibt.
Ein flüssiges Scroll-Erlebnis entscheidet oft über den ersten Eindruck einer App. Du willst genau das? Melde dich.
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
Room-Migrationen – das Schema ändern, ohne Nutzerdaten zu verlieren
Eine neue Spalte in der Entity, und beim nächsten Start ist der Spielstand weg – weil fallbackToDestructiveMigration die Datenbank einfach löscht. So machst du es richtig, inklusive Test.
stateIn und shareIn – aus einem kalten Flow wird geteilter Zustand
Jeder Sammler eines kalten Flows startet die Arbeit neu – zwei Beobachter, zwei Datenbankabfragen. stateIn und shareIn machen daraus einen Strom, den sich alle teilen, samt aktuellem Wert und Abschalten bei Inaktivität.
viewModelScope – Coroutinen, die mit dem Bildschirm verschwinden
Eine Coroutine, die nach dem Schließen des Bildschirms weiterläuft, schreibt in ein ViewModel, das niemand mehr sieht – und hält im schlimmsten Fall die ganze Activity im Speicher. viewModelScope beendet sie automatisch.