CompositionLocal – Werte durch den Compose-Baum reichen, ohne sie durchzuschleifen
Wenn ein Wert durch acht Composables durchgereicht wird, nur damit ihn das neunte benutzen kann, ist etwas schief. CompositionLocal löst das – aber nur für eine sehr bestimmte Sorte von Werten.
Kennst du diese Sorte Parameter? Er wird ganz oben erzeugt, dann durch HomeScreen, Inhalt, KartenListe, Karte und KartenFuss durchgereicht — und erst dort wirklich gebraucht. Alle Zwischenstationen tragen ihn nur mit. In Compose heißt das *prop drilling*, und es gibt ein Werkzeug dagegen.
Der Aufbau
// 1) Ein Typ, den du weiterreichen willst
data class Markenfarben(val akzent: Color, val warnung: Color)
// 2) Der Kanal – mit einem Standardwert, falls niemand etwas gesetzt hat
val LocalMarke = staticCompositionLocalOf {
Markenfarben(akzent = Color(0xFF0F766E), warnung = Color(0xFFB91C1C))
}Ganz oben füllst du ihn einmal:
@Composable
fun App(marke: Markenfarben) {
CompositionLocalProvider(LocalMarke provides marke) {
HomeScreen() // reicht nichts weiter
}
}Und irgendwo tief unten holst du ihn dir:
@Composable
fun KartenFuss() {
val marke = LocalMarke.current
Text("Mehr erfahren", color = marke.akzent)
}Kein Parameter mehr in HomeScreen, Inhalt, KartenListe und Karte. Genau das war das Ziel.
staticCompositionLocalOf oder compositionLocalOf?
Der Unterschied ist die Sorte Wert, die du transportierst:
staticCompositionLocalOf— für Werte, die sich praktisch nie ändern (Theme, Marke, ein Logger). Ändert sich der Wert doch, wird der gesamte Inhalt darunter neu gebaut. Dafür kostet das Lesen im Normalbetrieb gar nichts.compositionLocalOf— für Werte, die sich durchaus ändern. Compose merkt sich dann, wer den Wert wirklich liest, und baut nur diese Stellen neu.
Faustregel: Ändert sich der Wert seltener als einmal pro Bildschirm-Leben, nimm static.
Wofür ich es benutze – und wofür nicht
Compose selbst nutzt genau dieses Werkzeug für MaterialTheme, LocalContext, LocalDensity und LocalConfiguration. Das ist auch die Sorte Wert, für die es gedacht ist: Umgebung, nicht Daten.
Was ich dort nicht hineinlege:
- Den Zustand eines Bildschirms. Der gehört ins ViewModel und wird bewusst als Parameter übergeben.
- Alles, wovon ich möchte, dass man es beim Lesen der Funktionssignatur sieht.
CompositionLocalmacht Abhängigkeiten unsichtbar — das ist der Preis für die Bequemlichkeit.
Ein Composable, das nur seine Parameter benutzt, kann ich isoliert testen und in einer @Preview anzeigen. Eins, das heimlich fünf CompositionLocals liest, nicht mehr ohne Weiteres. Deshalb: sparsam einsetzen, und wenn, dann für Dinge, die wirklich „die Umgebung" sind.
Du planst eine App mit eigenem Markenlook über viele Screens hinweg? Genau dafür ist das gebaut. Schreib mir, wenn du dabei Unterstützung brauchst.
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
callbackFlow – Listener-APIs in einen Flow verwandeln
Android steckt voller Callback-APIs mit `register`/`unregister`. Mit `callbackFlow` machst du daraus einen ganz normalen Flow – inklusive automatischem Abmelden, wenn niemand mehr zuhört.
ModalBottomSheet in Material 3 – das Blatt, das von unten hereinfährt
Ein Menü, eine Detailansicht, eine Auswahl – auf dem Handy kommt so etwas von unten. Material 3 bringt das fertig mit, inklusive Wischgeste und Verdunkelung. Man muss nur wissen, wie man es richtig schließt.
Fehler in Flows abfangen – catch, retry und ein sinnvoller Fallback
Ein try/catch um einen Flow herum fängt nichts. Fehler laufen im Flow selbst mit und werden dort behandelt – mit catch für die Ausgabe und retry für den zweiten Versuch bei Netzproblemen.