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 é 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.
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.
@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.
| Forma | Código | Leitura | Escrita |
|---|---|---|---|
| Sem delegação | val count = mutableStateOf(0) | count.value | count.value = n |
| Com delegação | var count by mutableStateOf(0) | count | count = n |
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.
// 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("") }
}
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.
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.
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
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.
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 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.
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 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
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.
Leia também