mutableStateOf: Erstellung von beobachtbarem Zustand und Compose-Reaktivität

Autor: IT Sectr Veröffentlicht: 2026-06-28 Lesezeit: 7 Min.

mutableStateOf ist eine Funktion in Jetpack Compose, die einen Container für veränderlichen beobachtbaren Zustand erstellt. Wenn sich der Wert in diesem Container ändert, löst Compose automatisch die Neuzusammensetzung aller Komponenten aus, die diesen Zustand lesen. Ohne mutableStateOf könnte die UI nicht reaktiv auf Datenänderungen aktualisiert werden. Laut Google Android Developers, 2026 ist mutableStateOf der grundlegende Baustein für lokalen Zustand in Compose.

Wichtige Punkte

  • mutableStateOf erstellt einen MutableState-Container, der von Compose Runtime verfolgt wird
  • Neuzusammensetzung wird automatisch ausgelöst, wenn sich der value dieses State-Objekts ändert
  • Delegation über var ermöglicht die Verwendung von mutableStateOf ohne Zugriff auf .value
  • Schlüssel in remember(mutableStateOf) sind nicht nötig — State benachrichtigt Compose selbst über Änderungen
  • Snapshot-System garantiert Lesekonsistenz in Multithread-Umgebungen

Was ist mutableStateOf in Jetpack Compose

mutableStateOf ist eine Funktion aus dem Paket compose.runtime, die ein MutableState<T>-Objekt erstellt, das einen Wert speichert und Compose Runtime über Änderungen benachrichtigen kann. Signatur: fun <T> mutableStateOf(value: T, policy: SnapshotMutationPolicy<T> = structuralEquality()): MutableState<T>. Der Parameter policy bestimmt, wann eine Änderung als signifikant gilt — bei struktureller Gleichheit, referenzieller Gleichheit oder nie.

MutableState ist ein Interface mit einer einzigen Eigenschaft value: einem Getter zum Lesen und einem Setter zum Schreiben. Wenn der Setter aufgerufen wird, zeichnet Compose Runtime die Änderung in einem Snapshot auf und markiert alle Composable-Funktionen, die diese State-Variable lesen, als neuzusammensetzungsbedürftig. Dieser Prozess erfolgt synchron innerhalb eines einzigen Snapshot-Zyklus, was Zwischenzustände bei kaskadierenden Änderungen eliminiert.

Parameter policy — das zweite Argument von mutableStateOf, definiert das Vergleichsverhalten. structuralEquality() prüft equals() — dies ist das Standardverhalten. referentialEquality() prüft === (referenzielle Gleichheit). neverEqual() betrachtet jede Zuweisung als Änderung. Die Wahl der policy beeinflusst, ob bei Zuweisung desselben Werts eine Neuzusammensetzung ausgelöst wird.

Syntax und Deklarationsmöglichkeiten von mutableStateOf

Der einfachste Weg, beobachtbaren Zustand zu deklarieren, ist die Verwendung von mutableStateOf mit remember. Ohne remember würde jede Neuzusammensetzung einen neuen State erstellen, und alle vorherigen Änderungen gingen verloren. remember stellt sicher, dass derselbe MutableState eine Reihe von Neuzusammensetzungen überlebt, solange die Composable-Funktion in der Komposition verbleibt.

kotlin
@Composable
fun Counter() {
    // Ohne Delegation: Lesen/Schreiben über .value
    val count = remember { mutableStateOf(0) }
    Button(onClick = { count.value++ }) {
        Text("Count: ${count.value}")
    }
}

@Composable
fun CounterDelegated() {
    // Mit Delegation: var + by = Property Delegation
    var count by remember { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Count: $count")
    }
}

Der Unterschied zwischen den beiden Ansätzen ist syntaktisch. Property Delegation (by) verwendet eine Kotlin-Konvention: Der Compiler generiert getValue()- und setValue()-Aufrufe zum Lesen und Schreiben. Dies entspricht dem direkten Zugriff auf count.value, sieht aber aus wie die Arbeit mit einer normalen Variablen. Beide Ansätze sind funktional identisch: Compose verfolgt das Lesen im Getter und das Schreiben im Setter unabhängig von der Notation.

FormCodeLesenSchreiben
Ohne Delegationval count = mutableStateOf(0)count.valuecount.value = n
Mit Delegationvar count by mutableStateOf(0)countcount = n

Delegierte Eigenschaften und var

Der Mechanismus der delegierten Eigenschaften in Kotlin ist keine Compose-Funktion, sondern eine eingebaute Sprachfähigkeit. Jede Klasse kann die Operatoren getValue(thisRef, property) und setValue(thisRef, property, value) implementieren, wonach ihre Instanz mit dem Schlüsselwort by verwendet werden kann. MutableState funktioniert genau so: getValue gibt den aktuellen Wert zurück und setValue weist einen neuen zu.

Ein wichtiger Unterschied: val vs var. mutableStateOf kann sowohl val als auch var zugewiesen werden. Bei val (val count = mutableStateOf(0)) ist das MutableState-Objekt selbst unveränderlich, aber seine Eigenschaft value kann geändert werden. Bei var (var count by mutableStateOf(0)) erzeugt die Delegation die Illusion, mit einem primitiven Typ zu arbeiten, aber der Setter ruft tatsächlich setValue auf MutableState auf. Die Wahl zwischen val und var ist die Wahl zwischen explizitem und implizitem Zugriff auf .value.

State Delegation ist syntaktischer Zucker, der den Code vereinfacht, aber die Mechanik nicht ändert. Der Kotlin-Compiler übersetzt var x by mutableStateOf(0) in Getter/Setter, die mutableStateOf.getValue() und mutableStateOf.setValue() aufrufen. Im generierten Bytecode gibt es keinen Unterschied zwischen val und var mit by — beide arbeiten über denselben MutableState-Container.

kotlin
    // Benutzerdefinierter Delegat für Compose State
class ValidatedState<T>(initialValue: T) {
    private val state = mutableStateOf(initialValue)

    operator fun getValue(thisRef: Any?, property: KProperty<*>) = state.value

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        if (value != state.value) {
            state.value = value
        }
    }
}

@Composable
fun Test() {
    var text by remember { ValidatedState("") }
}

Snapshot-System: Wie mutableStateOf intern funktioniert

Snapshot ist ein Mechanismus von Compose Runtime, der die Lesekonsistenz von State bei parallelen Änderungen gewährleistet. Wenn eine Composable-Funktion mutableStateOf liest, zeichnet der Snapshot den aktuellen Wert auf. Wenn während der Komposition eine andere Änderung in denselben State schreibt, sieht der Snapshot die Schreiboperation, erlaubt aber kein Lesen inkonsistenter Daten — das Lesen gibt immer den zu Beginn des Snapshots gültigen Wert zurück.

Wenn der Setter mutableStateOf.value = newValue aufgerufen wird, löst Compose Runtime nicht sofort eine Neuzusammensetzung aus. Stattdessen wird die Änderung im aktuellen Snapshot registriert. Wenn der Snapshot angewendet wird (an der Framegrenze), durchläuft Compose die Liste der geänderten States und markiert die lesenden Komponenten als Invalid. Erst im nächsten Frame beginnt die Neuzusammensetzung. Dies garantiert, dass die UI bei kaskadierenden Änderungen nicht dutzende Male neu gezeichnet wird.

Globale und lokale Snapshots: Standardmäßig arbeitet mutableStateOf im globalen Snapshot, der automatisch angewendet wird. Sie können einen lokalen Snapshot über Snapshot.takeSnapshot() für isoliertes Lesen ohne Nebenwirkungen erstellen. Dies wird innerhalb von Modifier verwendet, wenn Sie State lesen müssen, ohne sich für Änderungen zu abonnieren. Dieser Ansatz optimiert die Leistung und verhindert unerwartete Neuzusammensetzungen.

Beispiele zur Verwendung von mutableStateOf

Betrachten wir ein reales Szenario — ein Login-Formular mit drei Feldern: E-Mail, Passwort und Ladezustand. Alle drei Felder verwenden mutableStateOf, jedoch mit unterschiedlichen policy und unterschiedlichen Verschachtelungsebenen. email verwendet Delegation, password verwendet direkten Zugriff.

kotlin
data class LoginState(
    val email: String = "",
    val password: String = "",
    val isLoading: Boolean = false,
    val error: String? = null
)

@Composable
fun LoginForm(onLogin: (String, String) -> Unit) {
    // Einzelner State für Formular, policy = referentialEquality
    var formState by remember {
        mutableStateOf(LoginState(), SnapshotMutationPolicy.referentialEquality())
    }

    val isValid = remember(formState) {
        formState.email.contains("@") && formState.password.length() >= 6
    }

    Column(modifier = Modifier.padding(16.dp)) {
        OutlinedTextField(
            value = formState.email,
            onValueChange = { formState = formState.copy(email = it) },
            label = { Text("E-Mail") }
        )
        OutlinedTextField(
            value = formState.password,
            onValueChange = { formState = formState.copy(password = it) },
            label = { Text("Passwort") },
            visualTransformation = PasswordVisualTransformation()
        )
        Button(
            onClick = { onLogin(formState.email, formState.password) },
            enabled = isValid
        ) {
            Text("Anmelden")
        }
    }
}

In diesem Beispiel wird mutableStateOf mit einer benutzerdefinierten Datenklasse LoginState und policy referentialEquality verwendet. Dies bedeutet, dass die Neuzusammensetzung nur ausgelöst wird, wenn eine neue LoginState-Instanz über copy() zugewiesen wird. isValid wird basierend auf formState berechnet und nur neu berechnet, wenn es sich ändert. Dieser Ansatz bietet eine klare Kontrolle über Neuzusammensetzungen: Jedes Formularfeld ändert sich nur durch Erstellen einer neuen Kopie.

Häufig gestellte Fragen

Was ist der Unterschied zwischen mutableStateOf und StateFlow?

mutableStateOf ist ein Compose-spezifischer Container, der innerhalb von Snapshots arbeitet. StateFlow stammt aus kotlinx.coroutines.flow und ist nicht an Compose gebunden. mutableStateOf löst automatisch eine Neuzusammensetzung aus, während StateFlow collectAsState() erfordert. Für den UI-Zustand innerhalb von Composable ist mutableStateOf vorzuziehen.

Kann mutableStateOf außerhalb von @Composable-Funktionen verwendet werden?

Ja, mutableStateOf kann außerhalb von Composable-Funktionen aufgerufen werden, wird dann aber nicht verfolgt. Für die Reaktivität in der UI muss State innerhalb von Composable gelesen werden. Viele ViewModels verwenden MutableStateField (einen Wrapper um mutableStateOf), um Zustand über StateFlow an die UI zu übergeben.

Was passiert, wenn State gleichzeitig von zwei Threads geändert wird?

Das Snapshot-System garantiert Konsistenz: Jede Neuzusammensetzung sieht einen konsistenten Zustand zu Beginn des Snapshots. Änderungen aus verschiedenen Threads werden atomar an der Framegrenze angewendet, was Wettlaufsituationen beim Lesen innerhalb einer einzelnen Komposition eliminiert.

Wie setzt man mutableStateOf auf den Anfangswert zurück?

Weisen Sie einen neuen Wert zu: count.value = 0 (oder count = 0 bei Delegation). Wenn Sie den State vollständig neu erstellen müssen, verwenden Sie remember mit einem Schlüssel: remember(key) { mutableStateOf(initial) } — wenn sich der Schlüssel ändert, wird der State neu erstellt.

Beeinträchtigt mutableStateOf die Leistung bei häufigen Änderungen?

Composer verwendet Snapshots, die Änderungen gruppieren: Selbst bei hunderten Zuweisungen in einem einzigen Frame wird die Neuzusammensetzung nur einmal ausgeführt. Für sehr häufige Aktualisierungen (Animationen) verwenden Sie Animatable oder animate*AsState — sie sind für frame-basierte Aktualisierungen optimiert.

Zusammenfassung

  • mutableStateOf erstellt einen beobachtbaren MutableState-Container, der von Compose Runtime verfolgt wird
  • Delegation über by vereinfacht den Code, ändert aber nicht die Mechanik von State
  • Snapshot-System garantiert Lesekonsistenz und verhindert unnötige Neuzusammensetzungen
  • Policy bestimmt, wann eine Änderung als signifikant gilt — structuralEquality, referentialEquality, neverEqual
  • remember ist zwingend erforderlich, um State zwischen Neuzusammensetzungen innerhalb von Composable zu erhalten
  • Empfehlung: Verwenden Sie mutableStateOf mit Delegation für UI-Zustand und referentialEquality für Datenklassen
  • Vermeiden Sie: Erstellen von mutableStateOf ohne remember — jede Zuweisung erstellt ein neues Objekt

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch