mutableStateOf: het creëren van waarneembare toestand en reactiviteit van Compose

Auteur: IT Sectr Gepubliceerd: 2026-06-28 Leestijd: 7 min

mutableStateOf is een functie in Jetpack Compose die een container van veranderlijke waarneembare toestand creëert. Wanneer de waarde in deze container verandert, start Compose automatisch de recompositie van alle componenten die deze toestand lezen. Zonder mutableStateOf zou de UI niet reactief kunnen updaten bij gegevenswijzigingen. Volgens Google Android Developers, 2026 is mutableStateOf de primaire bouwsteen voor lokale state in Compose.

Belangrijkste punten

  • mutableStateOf creëert een MutableState-container die wordt gevolgd door Compose Runtime
  • Recompositie wordt automatisch gestart bij wijziging van value van dit State-object
  • Delegatie via var maakt het mogelijk mutableStateOf te gebruiken zonder .value aan te spreken
  • Sleutels in remember(mutableStateOf) zijn niet nodig — State stelt Compose zelf op de hoogte van wijzigingen
  • Snapshot-systeem garandeert consistentie van lezen in een multi-thread omgeving

Wat is mutableStateOf in Jetpack Compose

mutableStateOf is een functie uit het compose.runtime-pakket die een MutableState<T>-object creëert dat een waarde opslaat en Compose Runtime op de hoogte kan stellen van wijzigingen. Handtekening: fun <T> mutableStateOf(value: T, policy: SnapshotMutationPolicy<T> = structuralEquality()): MutableState<T>. De parameter policy bepaalt wanneer een wijziging als significant wordt beschouwd — bij structurele gelijkheid, bij referentiële gelijkheid of nooit.

MutableState is een interface met een enkele eigenschap value: getter voor lezen en setter voor schrijven. Bij het aanroepen van de setter registreert Compose Runtime de wijziging in een snapshot en markeert alle Composable-functies die deze State-variabele lezen als recompositie vereisend. Dit proces vindt synchroon plaats binnen één snapshot-cyclus, waardoor tussentoestanden bij cascade-wijzigingen worden geëlimineerd.

Parameter policy — het tweede argument van mutableStateOf, dat het gedrag bij vergelijking bepaalt. structuralEquality() controleert equals() — dit is het standaardgedrag. referentialEquality() controleert === (referentiële gelijkheid). neverEqual() beschouwt elke toewijzing als een wijziging. De keuze van policy beïnvloedt of recompositie wordt gestart bij het toewijzen van dezelfde waarde.

Syntaxis en manieren om mutableStateOf te declareren

De eenvoudigste manier om waarneembare toestand te declareren is het gebruik van mutableStateOf met remember. Zonder remember zou bij elke recompositie een nieuwe State worden aangemaakt en zouden alle voorgaande wijzigingen verloren gaan. remember garandeert dat dezelfde MutableState een reeks recomposities overleeft zolang de Composable-functie in de compositie blijft.

kotlin
@Composable
fun Counter() {
    // Zonder delegatie: lezen/schrijven via .value
    val count = remember { mutableStateOf(0) }
    Button(onClick = { count.value++ }) {
        Text("Count: ${count.value}")
    }
}

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

Het verschil tussen de twee varianten is syntactisch. Property Delegation (by) gebruikt de Kotlin-conventie: de compiler genereert getValue()- en setValue()-aanroepen voor lezen en schrijven. Dit is gelijkwaardig aan directe toegang tot count.value, maar ziet eruit als werken met een gewone variabele. Beide varianten zijn functioneel identiek: Compose volgt lezen in de getter en schrijven in de setter, ongeacht de schrijfwijze.

VormCodeLezenSchrijven
Zonder delegatieval count = mutableStateOf(0)count.valuecount.value = n
Met delegatievar count by mutableStateOf(0)countcount = n

Gedelegeerde eigenschappen en var

Het mechanisme van gedelegeerde eigenschappen in Kotlin is geen kenmerk van Compose, maar een ingebouwde mogelijkheid van de taal. Elke klasse kan de operatoren getValue(thisRef, property) en setValue(thisRef, property, value) implementeren, waarna het exemplaar kan worden gebruikt met het sleutelwoord by. MutableState werkt precies zo: getValue retourneert de huidige waarde en setValue kent een nieuwe toe.

Belangrijk verschil: val en var. mutableStateOf kan zowel aan val als aan var worden toegewezen. In het geval van val (val count = mutableStateOf(0)) is het MutableState-object zelf onveranderlijk, maar de eigenschap value kan worden gewijzigd. In het geval van var (var count by mutableStateOf(0)) creëert delegatie de illusie van werken met een primitief, maar in werkelijkheid roept de setter setValue aan op MutableState. De keuze tussen val en var is een keuze tussen expliciete en impliciete toegang tot .value.

State Delegation is syntactische suiker die de code vereenvoudigt, maar de mechanica niet verandert. De Kotlin-compiler transpileert var x by mutableStateOf(0) naar getter/setter die mutableStateOf.getValue() en mutableStateOf.setValue() aanroepen. In de gegenereerde bytecode is er geen verschil tussen val en var met by — beide werken via dezelfde MutableState-container.

kotlin
    // Aangepaste delegatie voor 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-systeem: hoe mutableStateOf intern werkt

Snapshot is een mechanisme van Compose Runtime dat de consistentie van het lezen van State bij parallelle wijzigingen waarborgt. Wanneer een Composable-functie mutableStateOf leest, registreert de snapshot de huidige waarde. Als tijdens de compositie een andere wijziging in dezelfde State schrijft, ziet de snapshot de schrijving, maar staat het lezen van inconsistente gegevens niet toe — lezen retourneert altijd de waarde die actueel is op het moment van het starten van de snapshot.

Bij het aanroepen van de setter mutableStateOf.value = newValue start Compose Runtime niet onmiddellijk de recompositie. In plaats daarvan wordt de wijziging geregistreerd in de huidige snapshot. Wanneer de snapshot wordt toegepast (op de framegrens), doorloopt Compose de lijst van gewijzigde State en markeert de lezende componenten als Invalid. Pas in het volgende frame wordt de recompositie gestart. Dit garandeert dat de UI niet tientallen keren wordt hertekend bij cascade-wijzigingen.

Globale en lokale snapshots: standaard werkt mutableStateOf in een globale snapshot die automatisch wordt toegepast. Er kan een lokale snapshot worden gemaakt via Snapshot.takeSnapshot() voor geïsoleerd lezen zonder bijwerkingen. Dit wordt gebruikt binnen Modifier, waar State moet worden gelezen, maar niet moet worden geabonneerd op wijzigingen. Deze aanpak optimaliseert de prestaties en voorkomt onverwachte recomposities.

Voorbeelden van het gebruik van mutableStateOf

Laten we een realistisch scenario bekijken — een inlogformulier met drie velden: e-mail, wachtwoord en laadstatus. Alle drie de velden gebruiken mutableStateOf, maar met verschillende policy en verschillende nesting. e-mail gebruikt delegatie, wachtwoord — directe toegang.

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) {
    // Enkele State voor formulier, 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("Wachtwoord") },
            visualTransformation = PasswordVisualTransformation()
        )
        Button(
            onClick = { onLogin(formState.email, formState.password) },
            enabled = isValid
        ) {
            Text("Inloggen")
        }
    }
}

In dit voorbeeld wordt mutableStateOf gebruikt met een aangepaste data-klasse LoginState en policy referentialEquality. Dit betekent dat recompositie alleen wordt gestart bij het toewijzen van een nieuw LoginState-exemplaar via copy(). isValid wordt berekend op basis van formState en alleen herberekend wanneer deze verandert. Deze aanpak geeft duidelijke controle over recomposities: elk formulierveld verandert alleen door het maken van een nieuwe kopie.

Veelgestelde vragen

Wat is het verschil tussen mutableStateOf en StateFlow?

mutableStateOf is een Compose-specifieke container die binnen snapshots werkt. StateFlow komt van kotlinx.coroutines.flow en is niet gebonden aan Compose. mutableStateOf start automatisch recompositie, StateFlow vereist collectAsState(). Voor UI-state binnen Composable heeft mutableStateOf de voorkeur.

Kan mutableStateOf buiten @Composable-functies worden gebruikt?

Ja, mutableStateOf kan buiten een Composable-functie worden aangeroepen, maar wordt dan niet gevolgd. Voor reactiviteit in de UI moet State binnen Composable worden gelezen. Veel ViewModels gebruiken MutableStateField (een wrapper over mutableStateOf) om state naar de UI te sturen via StateFlow.

Wat gebeurt er bij gelijktijdige wijziging van State vanuit twee threads?

Snapshot systeem garandeert consistentie: elke recompositie ziet de consistente toestand op het moment van starten van de snapshot. Wijzigingen vanuit verschillende threads worden atomair toegepast op de framegrens, wat race conditions bij het lezen binnen één compositie elimineert.

Hoe mutableStateOf terugzetten naar de beginwaarde?

Wijs een nieuwe waarde toe: count.value = 0 (of count = 0 bij delegatie). Als volledige hercreatie van State nodig is — gebruik remember met een sleutel: remember(key) { mutableStateOf(initial) } — bij wijziging van de sleutel wordt State opnieuw aangemaakt.

Beïnvloedt mutableStateOf de prestaties bij frequente wijzigingen?

Composer gebruikt snapshots die wijzigingen groeperen: zelfs met honderd toewijzingen in één frame wordt recompositie slechts één keer uitgevoerd. Voor zeer frequente updates (animaties) gebruikt u Animatable of animate*AsState — deze zijn geoptimaliseerd voor frame-updates.

Samenvatting

  • mutableStateOf creëert een waarneembare MutableState-container die wordt gevolgd door Compose Runtime
  • Delegatie via by vereenvoudigt de code, maar verandert de mechanica van State niet
  • Snapshot systeem garandeert leesconsistentie en voorkomt onnodige recomposities
  • Policy bepaalt wanneer een wijziging als significant wordt beschouwd — structuralEquality, referentialEquality, neverEqual
  • remember is verplicht om State tussen recomposities binnen Composable te behouden
  • Aanbeveling: gebruik mutableStateOf met delegatie voor UI-state en referentialEquality voor dataklassen
  • Vermijd: het maken van mutableStateOf zonder remember — elke toewijzing zal een nieuw object creëren

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook