mutableStateOf: crearea stării observabile și reactivitatea Compose

Autor: IT Sectr Publicat: 2026-06-28 Timp de citire: 7 min

mutableStateOf este o funcție în Jetpack Compose care creează un container de stare observabilă mutabilă. Când valoarea din acest container se schimbă, Compose declanșează automat recompoziția tuturor componentelor care citesc această stare. Fără mutableStateOf, UI nu s-ar putea actualiza reactiv la modificările datelor. Conform Google Android Developers, 2026, mutableStateOf este blocul de construcție principal pentru starea locală în Compose.

Principalele

  • mutableStateOf creează un container MutableState urmărit de Compose Runtime
  • Recompoziția se declanșează automat la modificarea value a acestui obiect State
  • Delegarea prin var permite utilizarea mutableStateOf fără a accesa .value
  • Cheile în remember(mutableStateOf) nu sunt necesare — State anunță singur Compose despre modificări
  • Sistemul Snapshot garantează consistența citirii în mediu multi-thread

Ce este mutableStateOf în Jetpack Compose

mutableStateOf este o funcție din pachetul compose.runtime care creează un obiect MutableState<T> ce stochează o valoare și poate notifica Compose Runtime despre modificări. Semnătura: fun <T> mutableStateOf(value: T, policy: SnapshotMutationPolicy<T> = structuralEquality()): MutableState<T>. Parametrul policy determină când o modificare este considerată semnificativă — la egalitate structurală, la egalitate referențială sau niciodată.

MutableState este o interfață cu o singură proprietate value: getter pentru citire și setter pentru scriere. La apelarea setter-ului, Compose Runtime înregistrează modificarea într-un snapshot și marchează toate funcțiile Composable care citesc această variabilă State ca necesitând recompoziție. Acest proces are loc sincron într-un singur ciclu de snapshot, ceea ce elimină stările intermediare la modificări în cascadă.

Parameter policy — al doilea argument al mutableStateOf, care determină comportamentul la comparare. structuralEquality() verifică equals() — acesta este comportamentul implicit. referentialEquality() verifică === (egalitate referențială). neverEqual() consideră fiecare atribuire ca o modificare. Alegerea policy influențează dacă recompoziția va fi declanșată la atribuirea aceleiași valori.

Sintaxa și modurile de declarare a mutableStateOf

Cel mai simplu mod de a declara stare observabilă este utilizarea mutableStateOf cu remember. Fără remember, la fiecare recompoziție s-ar crea un nou State și toate modificările anterioare s-ar pierde. remember garantează că același MutableState supraviețuiește unei serii de recompoziții atâta timp cât funcția Composable rămâne în compoziție.

kotlin
@Composable
fun Counter() {
    // Fără delegare: citire/scriere prin .value
    val count = remember { mutableStateOf(0) }
    Button(onClick = { count.value++ }) {
        Text("Count: ${count.value}")
    }
}

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

Diferența dintre cele două variante este sintactică. Property Delegation (by) folosește convenția Kotlin: compilatorul generează apeluri getValue() și setValue() pentru citire și scriere. Aceasta este echivalentă cu accesarea directă a count.value, dar arată ca lucrul cu o variabilă obișnuită. Ambele variante sunt funcțional identice: Compose urmărește citirea în getter și scrierea în setter, indiferent de forma de scriere.

FormăCodCitireScriere
Fără delegareval count = mutableStateOf(0)count.valuecount.value = n
Cu delegarevar count by mutableStateOf(0)countcount = n

Proprietăți delegate și var

Mecanismul proprietăților delegate din Kotlin nu este o caracteristică a Compose, ci o capacitate încorporată a limbajului. Orice clasă poate implementa operatorii getValue(thisRef, property) și setValue(thisRef, property, value), după care instanța sa poate fi folosită cu cuvântul cheie by. MutableState exact așa funcționează: getValue returnează valoarea curentă, iar setValue atribuie una nouă.

Diferența importantă: val și var. mutableStateOf poate fi atribuit atât la val, cât și la var. În cazul val (val count = mutableStateOf(0)), obiectul MutableState în sine este imutabil, dar proprietatea sa value poate fi modificată. În cazul var (var count by mutableStateOf(0)), delegarea creează iluzia lucrului cu un primitiv, dar de fapt setter-ul apelează setValue pe MutableState. Alegerea între val și var este o alegere între accesul explicit și implicit la .value.

State Delegation este zahăr sintactic care simplifică codul, dar nu schimbă mecanica. Compilatorul Kotlin transpune var x by mutableStateOf(0) în getter/setter care apelează mutableStateOf.getValue() și mutableStateOf.setValue(). În bytecode-ul generat, nu există diferență între val și var cu by — ambele funcționează prin același container MutableState.

kotlin
    // Delegare personalizată pentru State în Compose
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("") }
}

Sistemul Snapshot: cum funcționează mutableStateOf în interior

Snapshot este un mecanism al Compose Runtime care asigură consistența citirii State la modificări paralele. Când o funcție Composable citește mutableStateOf, snapshot-ul înregistrează valoarea curentă. Dacă în timpul compoziției o altă modificare scrie în același State, snapshot-ul vede scrierea, dar nu permite citirea datelor inconsistente — citirea returnează întotdeauna valoarea actuală la momentul începerii snapshot-ului.

La apelarea setter-ului mutableStateOf.value = newValue, Compose Runtime nu declanșează imediat recompoziția. În schimb, modificarea este înregistrată în snapshot-ul curent. Când snapshot-ul este aplicat (la granița frame-ului), Compose parcurge lista de State-uri modificate și marchează componentele cititoare ca Invalid. Abia în următorul frame se declanșează recompoziția. Aceasta garantează că UI nu este redesenat de zeci de ori la modificări în cascadă.

Snapshot-uri globale și locale: în mod implicit, mutableStateOf funcționează într-un snapshot global care se aplică automat. Se poate crea un snapshot local prin Snapshot.takeSnapshot() pentru citire izolată fără efecte secundare. Aceasta este utilizat în interiorul Modifier, unde trebuie citit State, dar nu trebuie să te abonezi la modificări. Această abordare optimizează performanța și previne recompozițiile neașteptate.

Exemple de utilizare a mutableStateOf

Să analizăm un scenariu real — un formular de autentificare cu trei câmpuri: email, parolă și stare de încărcare. Toate cele trei câmpuri folosesc mutableStateOf, dar cu policy diferite și grad diferit de imbricare. email folosește delegare, parola — acces direct.

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) {
    // State unic pentru 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("Email") }
        )
        OutlinedTextField(
            value = formState.password,
            onValueChange = { formState = formState.copy(password = it) },
            label = { Text("Parolă") },
            visualTransformation = PasswordVisualTransformation()
        )
        Button(
            onClick = { onLogin(formState.email, formState.password) },
            enabled = isValid
        ) {
            Text("Autentificare")
        }
    }
}

În acest exemplu, mutableStateOf este folosit cu o clasă de date personalizată LoginState și policy referentialEquality. Aceasta înseamnă că recompoziția se va declanșa doar la atribuirea unei noi instanțe LoginState prin copy(). isValid este calculat pe baza formState și se recalculează doar la modificarea acestuia. Această abordare oferă un control clar asupra recompozițiilor: fiecare câmp al formularului se modifică doar prin crearea unei noi copii.

Întrebări frecvente

Cu ce diferă mutableStateOf de StateFlow?

mutableStateOf este un container specific Compose care funcționează în interiorul snapshot-urilor. StateFlow provine din kotlinx.coroutines.flow și nu este legat de Compose. mutableStateOf declanșează automat recompoziția, StateFlow necesită collectAsState(). Pentru starea UI în interiorul Composable, mutableStateOf este preferat.

Poate fi folosit mutableStateOf în afara funcțiilor @Composable?

Da, mutableStateOf poate fi apelat în afara funcției Composable, dar nu va fi urmărit. Pentru funcționarea reactivității în UI, trebuie citit State în interiorul Composable. Multe ViewModel-uri folosesc MutableStateField (o înfășurare peste mutableStateOf) pentru a transmite stare către UI prin StateFlow.

Ce se întâmplă la modificarea simultană a State din două thread-uri?

Snapshot sistemul garantează consistența: fiecare recompoziție vede starea coerentă la momentul începerii snapshot-ului. Modificările din thread-uri diferite se aplică atomic la granița frame-ului, ceea ce elimină race condition la citirea în cadrul unei compoziții.

Cum resetăm mutableStateOf la valoarea inițială?

Atribuiți o nouă valoare: count.value = 0 (sau count = 0 la delegare). Dacă este necesară recrearea completă a State — folosiți remember cu cheie: remember(key) { mutableStateOf(initial) } — la schimbarea cheii, State va fi creat din nou.

mutableStateOf afectează performanța la modificări frecvente?

Composer folosește snapshot-uri care grupează modificările: chiar și cu o sută de atribuiri într-un singur frame, recompoziția se execută o singură dată. Pentru actualizări foarte frecvente (animații), folosiți Animatable sau animate*AsState — sunt optimizate pentru actualizări pe cadre.

Concluzii

  • mutableStateOf creează un container observabil MutableState urmărit de Compose Runtime
  • Delegarea prin by simplifică codul, dar nu schimbă mecanica de funcționare a State
  • Snapshot sistemul garantează consistența citirii și previne recompozițiile inutile
  • Policy determină când o modificare este considerată semnificativă — structuralEquality, referentialEquality, neverEqual
  • remember este obligatoriu pentru păstrarea State între recompoziții în interiorul Composable
  • Recomandare: folosiți mutableStateOf cu delegare pentru starea UI și referentialEquality pentru clasele de date
  • Evitați: crearea mutableStateOf fără remember — fiecare atribuire va crea un obiect nou

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și