mutableStateOf: creazione di stato osservabile e reattività di Compose

Autore: IT Sectr Pubblicato: 2026-06-28 Tempo di lettura: 7 min

mutableStateOf è una funzione in Jetpack Compose che crea un contenitore di stato mutabile osservabile. Quando il valore all'interno di questo contenitore cambia, Compose attiva automaticamente la ricomposizione di tutti i componenti che leggono questo stato. Senza mutableStateOf, l'interfaccia utente non potrebbe aggiornarsi in modo reattivo quando i dati cambiano. Secondo Google Android Developers, 2026, mutableStateOf è il blocco di costruzione principale per lo stato locale in Compose.

Punti chiave

  • mutableStateOf crea un contenitore MutableState tracciato da Compose Runtime
  • Ricomposizione viene attivata automaticamente quando il value di questo oggetto State cambia
  • Delega tramite var consente di utilizzare mutableStateOf senza accedere a .value
  • Chiavi in remember(mutableStateOf) non sono necessarie — State stesso notifica Compose delle modifiche
  • Sistema Snapshot garantisce consistenza di lettura in ambienti multi-thread

Cos'è mutableStateOf in Jetpack Compose

mutableStateOf è una funzione del pacchetto compose.runtime che crea un oggetto MutableState<T> che memorizza un valore e può notificare Compose Runtime delle modifiche. Firma: fun <T> mutableStateOf(value: T, policy: SnapshotMutationPolicy<T> = structuralEquality()): MutableState<T>. Il parametro policy determina quando una modifica è considerata significativa — per uguaglianza strutturale, uguaglianza referenziale, o mai.

MutableState è un'interfaccia con una singola proprietà value: un getter per la lettura e un setter per la scrittura. Quando il setter viene chiamato, Compose Runtime registra la modifica in uno snapshot e segna tutte le funzioni Composable che leggono questa variabile State come bisognose di ricomposizione. Questo processo avviene in modo sincrono all'interno di un singolo ciclo di snapshot, eliminando gli stati intermedi durante le modifiche a cascata.

Parametro policy — il secondo argomento di mutableStateOf, definisce il comportamento di confronto. structuralEquality() verifica equals() — questo è il comportamento predefinito. referentialEquality() verifica === (uguaglianza referenziale). neverEqual() considera ogni assegnazione come una modifica. La scelta della policy influisce sul fatto che la ricomposizione venga attivata quando si assegna lo stesso valore.

Sintassi e modi di dichiarare mutableStateOf

Il modo più semplice per dichiarare uno stato osservabile è usare mutableStateOf con remember. Senza remember, ogni ricomposizione creerebbe un nuovo State e tutte le modifiche precedenti andrebbero perse. remember garantisce che lo stesso MutableState sopravviva a una serie di ricomposizioni finché la funzione Composable rimane nella composizione.

kotlin
@Composable
fun Counter() {
    // Senza delega: lettura/scrittura tramite .value
    val count = remember { mutableStateOf(0) }
    Button(onClick = { count.value++ }) {
        Text("Count: ${count.value}")
    }
}

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

La differenza tra i due approcci è sintattica. Property Delegation (by) usa una convenzione Kotlin: il compilatore genera chiamate getValue() e setValue() per lettura e scrittura. Ciò equivale ad accedere direttamente a count.value ma sembra di lavorare con una variabile normale. Entrambi gli approcci sono funzionalmente identici: Compose traccia la lettura nel getter e la scrittura nel setter indipendentemente dalla notazione.

FormaCodiceLetturaScrittura
Senza delegaval count = mutableStateOf(0)count.valuecount.value = n
Con delegavar count by mutableStateOf(0)countcount = n

Proprietà delegate e var

Il meccanismo delle proprietà delegate di Kotlin non è una caratteristica di Compose ma una capacità incorporata del linguaggio. Qualsiasi classe può implementare gli operatori getValue(thisRef, property) e setValue(thisRef, property, value), dopodiché la sua istanza può essere utilizzata con la parola chiave by. MutableState funziona esattamente così: getValue restituisce il valore corrente e setValue ne assegna uno nuovo.

Una distinzione importante: val vs var. mutableStateOf può essere assegnato sia a val che a var. Con val (val count = mutableStateOf(0)), l'oggetto MutableState stesso è immutabile, ma la sua proprietà value può essere modificata. Con var (var count by mutableStateOf(0)), la delega crea l'illusione di lavorare con un primitivo, ma il setter in realtà chiama setValue su MutableState. Scegliere tra val e var significa scegliere tra accesso esplicito e implicito a .value.

State Delegation è zucchero sintattico che semplifica il codice ma non cambia la meccanica. Il compilatore Kotlin traduce var x by mutableStateOf(0) in getter/setter che chiamano mutableStateOf.getValue() e mutableStateOf.setValue(). Nel bytecode generato, non c'è differenza tra val e var con by — entrambi funzionano attraverso lo stesso contenitore MutableState.

kotlin
    // Delegato personalizzato per 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("") }
}

Sistema Snapshot: come funziona mutableStateOf internamente

Snapshot è un meccanismo di Compose Runtime che garantisce la consistenza di lettura dello State durante modifiche parallele. Quando una funzione Composable legge mutableStateOf, lo snapshot registra il valore corrente. Se durante la composizione un'altra modifica scrive nello stesso State, lo snapshot vede la scrittura ma non permette di leggere dati inconsistenti — la lettura restituisce sempre il valore valido all'inizio dello snapshot.

Quando il setter mutableStateOf.value = newValue viene chiamato, Compose Runtime non attiva immediatamente la ricomposizione. Invece, la modifica viene registrata nello snapshot corrente. Quando lo snapshot viene applicato (al limite del frame), Compose scorre l'elenco degli State modificati e segna i componenti lettori come Invalid. Solo nel frame successivo inizia la ricomposizione. Questo garantisce che l'interfaccia utente non venga ridisegnata decine di volte durante modifiche a cascata.

Snapshot globali e locali: per impostazione predefinita, mutableStateOf funziona nello snapshot globale, che viene applicato automaticamente. Puoi creare uno snapshot locale tramite Snapshot.takeSnapshot() per lettura isolata senza effetti collaterali. Questo viene utilizzato all'interno di Modifier quando è necessario leggere State senza sottoscriversi alle modifiche. Questo approccio ottimizza le prestazioni e previene ricomposizioni inaspettate.

Esempi di utilizzo di mutableStateOf

Consideriamo uno scenario reale — un modulo di login con tre campi: email, password e stato di caricamento. Tutti e tre i campi utilizzano mutableStateOf, ma con diverse policy e diversi livelli di annidamento. email usa la delega, password usa l'accesso diretto.

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 singolo per il modulo, 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("Password") },
            visualTransformation = PasswordVisualTransformation()
        )
        Button(
            onClick = { onLogin(formState.email, formState.password) },
            enabled = isValid
        ) {
            Text("Accedi")
        }
    }
}

In questo esempio, mutableStateOf viene utilizzato con una classe dati personalizzata LoginState e policy referentialEquality. Ciò significa che la ricomposizione verrà attivata solo quando viene assegnata una nuova istanza di LoginState tramite copy(). isValid viene calcolato in base a formState e viene ricalcolato solo quando cambia. Questo approccio fornisce un controllo chiaro sulle ricomposizioni: ogni campo del modulo cambia solo tramite la creazione di una nuova copia.

Domande frequenti

Qual è la differenza tra mutableStateOf e StateFlow?

mutableStateOf è un contenitore specifico di Compose che funziona all'interno degli snapshot. StateFlow proviene da kotlinx.coroutines.flow e non è legato a Compose. mutableStateOf attiva automaticamente la ricomposizione, mentre StateFlow richiede collectAsState(). Per lo stato dell'interfaccia utente all'interno di Composable, mutableStateOf è preferibile.

Si può usare mutableStateOf al di fuori delle funzioni @Composable?

Sì, mutableStateOf può essere chiamato al di fuori delle funzioni Composable, ma non verrà tracciato. Per la reattività nell'interfaccia utente, lo State deve essere letto all'interno di Composable. Molte ViewModel utilizzano MutableStateField (un wrapper attorno a mutableStateOf) per passare lo stato all'interfaccia utente tramite StateFlow.

Cosa succede quando lo State viene modificato contemporaneamente da due thread?

Il sistema Snapshot garantisce consistenza: ogni ricomposizione vede uno stato consistente all'inizio dello snapshot. Le modifiche da thread diversi vengono applicate atomicamente al limite del frame, eliminando le race condition durante le letture all'interno di una singola composizione.

Come ripristinare mutableStateOf al valore iniziale?

Assegna un nuovo valore: count.value = 0 (o count = 0 con delega). Se hai bisogno di ricreare completamente lo State, usa remember con una chiave: remember(key) { mutableStateOf(initial) } — quando la chiave cambia, lo State verrà creato di nuovo.

mutableStateOf influisce sulle prestazioni con modifiche frequenti?

Composer utilizza snapshot che raggruppano le modifiche: anche con centinaia di assegnazioni in un singolo frame, la ricomposizione viene eseguita una sola volta. Per aggiornamenti molto frequenti (animazioni), usa Animatable o animate*AsState — sono ottimizzati per aggiornamenti frame per frame.

Riepilogo

  • mutableStateOf crea un contenitore MutableState osservabile tracciato da Compose Runtime
  • Delega tramite by semplifica il codice ma non cambia la meccanica dello State
  • Sistema Snapshot garantisce consistenza di lettura e previene ricomposizioni non necessarie
  • Policy determina quando una modifica è considerata significativa — structuralEquality, referentialEquality, neverEqual
  • remember è obbligatorio per preservare lo State tra le ricomposizioni all'interno di Composable
  • Raccomandazione: usa mutableStateOf con delega per lo stato dell'interfaccia utente e referentialEquality per le classi dati
  • Evita: creare mutableStateOf senza remember — ogni assegnazione creerà un nuovo oggetto

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche