mutableStateOf: criação de estado observável e reatividade do Compose

Autor: IT Sectr Publicado: 2026-06-28 Tempo de leitura: 7 min

mutableStateOf é uma função no Jetpack Compose que cria um contêiner de estado mutável observável. Quando o valor dentro deste contêiner muda, o Compose automaticamente aciona a recomposição de todos os componentes que leem este estado. Sem mutableStateOf, a interface do usuário não poderia ser atualizada reativamente quando os dados mudam. De acordo com Google Android Developers, 2026, mutableStateOf é o bloco de construção principal para estado local no Compose.

Pontos principais

  • mutableStateOf cria um contêiner MutableState rastreado pelo Compose Runtime
  • Recomposição é acionada automaticamente quando o value deste objeto State muda
  • Delegação via var permite usar mutableStateOf sem acessar .value
  • Chaves em remember(mutableStateOf) não são necessárias — o próprio State notifica o Compose sobre alterações
  • Sistema Snapshot garante consistência de leitura em ambientes multi-thread

O que é mutableStateOf no Jetpack Compose

mutableStateOf é uma função do pacote compose.runtime que cria um objeto MutableState<T> que armazena um valor e pode notificar o Compose Runtime sobre alterações. Assinatura: fun <T> mutableStateOf(value: T, policy: SnapshotMutationPolicy<T> = structuralEquality()): MutableState<T>. O parâmetro policy determina quando uma alteração é considerada significativa — por igualdade estrutural, igualdade referencial, ou nunca.

MutableState é uma interface com uma única propriedade value: um getter para leitura e um setter para escrita. Quando o setter é chamado, o Compose Runtime registra a alteração em um snapshot e marca todas as funções Composable que leem esta variável State como necessitando de recomposição. Este processo ocorre de forma síncrona dentro de um único ciclo de snapshot, o que elimina estados intermediários durante alterações em cascata.

Parâmetro policy — o segundo argumento de mutableStateOf, define o comportamento de comparação. structuralEquality() verifica equals() — este é o comportamento padrão. referentialEquality() verifica === (igualdade referencial). neverEqual() considera cada atribuição como uma alteração. A escolha da policy afeta se a recomposição será acionada ao atribuir o mesmo valor.

Sintaxe e formas de declarar mutableStateOf

A maneira mais simples de declarar estado observável é usar mutableStateOf com remember. Sem remember, cada recomposição criaria um novo State e todas as alterações anteriores seriam perdidas. remember garante que o mesmo MutableState sobreviva a uma série de recomposições enquanto a função Composable permanecer na composição.

kotlin
@Composable
fun Counter() {
    // Sem delegação: leitura/escrita via .value
    val count = remember { mutableStateOf(0) }
    Button(onClick = { count.value++ }) {
        Text("Count: ${count.value}")
    }
}

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

A diferença entre as duas abordagens é sintática. Property Delegation (by) usa uma convenção do Kotlin: o compilador gera chamadas getValue() e setValue() para leitura e escrita. Isso é equivalente a acessar diretamente count.value, mas parece trabalhar com uma variável normal. Ambas as abordagens são funcionalmente idênticas: o Compose rastreia a leitura no getter e a escrita no setter independentemente da notação.

FormaCódigoLeituraEscrita
Sem delegaçãoval count = mutableStateOf(0)count.valuecount.value = n
Com delegaçãovar count by mutableStateOf(0)countcount = n

Propriedades delegadas e var

O mecanismo de propriedades delegadas do Kotlin não é um recurso do Compose, mas uma capacidade incorporada da linguagem. Qualquer classe pode implementar os operadores getValue(thisRef, property) e setValue(thisRef, property, value), após o qual sua instância pode ser usada com a palavra-chave by. MutableState funciona exatamente assim: getValue retorna o valor atual e setValue atribui um novo.

Uma distinção importante: val vs var. mutableStateOf pode ser atribuído tanto a val quanto a var. Com val (val count = mutableStateOf(0)), o objeto MutableState em si é imutável, mas sua propriedade value pode ser alterada. Com var (var count by mutableStateOf(0)), a delegação cria a ilusão de trabalhar com um primitivo, mas o setter na verdade chama setValue no MutableState. Escolher entre val e var é escolher entre acesso explícito e implícito a .value.

State Delegation é açúcar sintático que simplifica o código, mas não altera a mecânica. O compilador Kotlin traduz var x by mutableStateOf(0) em getter/setter que chamam mutableStateOf.getValue() e mutableStateOf.setValue(). No bytecode gerado, não há diferença entre val e var com by — ambos funcionam através do mesmo contêiner MutableState.

kotlin
    // Delegado personalizado para 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: como mutableStateOf funciona internamente

Snapshot é um mecanismo do Compose Runtime que garante consistência de leitura do State durante alterações paralelas. Quando uma função Composable lê mutableStateOf, o snapshot registra o valor atual. Se durante a composição outra alteração escrever no mesmo State, o snapshot vê a escrita, mas não permite ler dados inconsistentes — a leitura sempre retorna o valor válido no início do snapshot.

Quando o setter mutableStateOf.value = newValue é chamado, o Compose Runtime não aciona imediatamente a recomposição. Em vez disso, a alteração é registrada no snapshot atual. Quando o snapshot é aplicado (no limite do quadro), o Compose percorre a lista de States alterados e marca os componentes leitores como Invalid. Apenas no próximo quadro a recomposição começa. Isso garante que a interface do usuário não seja redesenhada dezenas de vezes durante alterações em cascata.

Snapshots globais e locais: por padrão, mutableStateOf funciona no snapshot global, que é aplicado automaticamente. Você pode criar um snapshot local via Snapshot.takeSnapshot() para leitura isolada sem efeitos colaterais. Isso é usado dentro de Modifier quando você precisa ler State sem se inscrever em alterações. Esta abordagem otimiza o desempenho e previne recomposições inesperadas.

Exemplos de uso do mutableStateOf

Vamos considerar um cenário real — um formulário de login com três campos: email, senha e status de carregamento. Todos os três campos usam mutableStateOf, mas com diferentes policy e diferentes níveis de aninhamento. email usa delegação, password usa acesso direto.

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) {
    // Estado único para o formulário, 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("Senha") },
            visualTransformation = PasswordVisualTransformation()
        )
        Button(
            onClick = { onLogin(formState.email, formState.password) },
            enabled = isValid
        ) {
            Text("Login")
        }
    }
}

Neste exemplo, mutableStateOf é usado com uma classe de dados personalizada LoginState e policy referentialEquality. Isso significa que a recomposição será acionada apenas quando uma nova instância de LoginState for atribuída via copy(). isValid é calculado com base em formState e é recalculado apenas quando ele muda. Esta abordagem fornece controle claro sobre as recomposições: cada campo do formulário muda apenas através da criação de uma nova cópia.

Perguntas frequentes

Qual a diferença entre mutableStateOf e StateFlow?

mutableStateOf é um contêiner específico do Compose que funciona dentro de snapshots. StateFlow vem de kotlinx.coroutines.flow e não está vinculado ao Compose. mutableStateOf aciona automaticamente a recomposição, enquanto StateFlow requer collectAsState(). Para estado de UI dentro de Composable, mutableStateOf é preferível.

Pode-se usar mutableStateOf fora de funções @Composable?

Sim, mutableStateOf pode ser chamado fora de funções Composable, mas não será rastreado. Para reatividade na interface, o State deve ser lido dentro de Composable. Muitas ViewModels usam MutableStateField (um wrapper sobre mutableStateOf) para passar estado para a UI via StateFlow.

O que acontece quando o State é alterado simultaneamente de duas threads?

O sistema Snapshot garante consistência: cada recomposição vê um estado consistente no início do snapshot. Alterações de diferentes threads são aplicadas atomicamente no limite do quadro, eliminando condições de corrida durante leituras dentro de uma única composição.

Como redefinir mutableStateOf para o valor inicial?

Atribua um novo valor: count.value = 0 (ou count = 0 com delegação). Se precisar recriar completamente o State, use remember com uma chave: remember(key) { mutableStateOf(initial) } — quando a chave mudar, o State será criado novamente.

O mutableStateOf afeta o desempenho com alterações frequentes?

O Composer usa snapshots que agrupam alterações: mesmo com centenas de atribuições em um único quadro, a recomposição é executada apenas uma vez. Para atualizações muito frequentes (animações), use Animatable ou animate*AsState — eles são otimizados para atualizações quadro a quadro.

Resumo

  • mutableStateOf cria um contêiner MutableState observável rastreado pelo Compose Runtime
  • Delegação via by simplifica o código, mas não altera a mecânica do State
  • Sistema Snapshot garante consistência de leitura e previne recomposições desnecessárias
  • Policy determina quando uma alteração é considerada significativa — structuralEquality, referentialEquality, neverEqual
  • remember é obrigatório para preservar State entre recomposições dentro de Composable
  • Recomendação: use mutableStateOf com delegação para estado de UI e referentialEquality para classes de dados
  • Evite: criar mutableStateOf sem remember — cada atribuição criará um novo objeto

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também