O que é State em Kotlin?
No contexto do Jetpack Compose, State (estado) é um valor que, quando muda, aciona automaticamente a recomposicao das funções Composable que o leem. O Compose rastreia quais Composables dependem de quais estados e reexecuta apenas os afetados quando o estado muda.
Sem estado reativo, a UI seria estática. Com State, você declara como a UI deve parecer para cada possível valor do estado, é o Compose cuida de manter tudo sincronizado.
Criando estado com mutableStateOf
import androidx.compose.runtime.*
@Composable
fun Contador() {
// Cria um estado reativo que sobrevive a recomposicoes
var contagem by remember { mutableStateOf(0) }
Column {
Text(text = "Contagem: $contagem")
Button(onClick = { contagem++ }) {
Text("Incrementar")
}
}
}
Quando contagem muda (via contagem++), o Compose detecta a mudanca e reexecuta Contador(), atualizando o Text com o novo valor.
Tres elementos trabalham juntos:
mutableStateOf(0): cria um holder de estado reativo com valor inicial 0.remember: preserva o estado entre recomposicoes (semremember, o estado seria recriado a cada vez).by: delegação de propriedade que permite ler e escrever o valor diretamente.
remember vs rememberSaveable
@Composable
fun CampoDeTexto() {
// remember: sobrevive a recomposicoes, mas NAO a mudancas de configuracao
var texto by remember { mutableStateOf("") }
// rememberSaveable: sobrevive a recomposicoes E mudancas de configuracao
var textoSalvo by rememberSaveable { mutableStateOf("") }
Column {
TextField(value = texto, onValueChange = { texto = it })
TextField(value = textoSalvo, onValueChange = { textoSalvo = it })
}
}
Use rememberSaveable quando o estado precisa sobreviver a rotação de tela ou a recriação da Activity.
State hoisting (elevacao de estado)
O padrão fundamental de gerenciamento de estado em Compose e elevar o estado para o componente pai:
// Componente stateless: recebe estado e eventos como parametros
@Composable
fun CampoDeNome(
nome: String,
onNomeMudou: (String) -> Unit,
modifier: Modifier = Modifier
) {
OutlinedTextField(
value = nome,
onValueChange = onNomeMudou,
label = { Text("Nome") },
modifier = modifier.fillMaxWidth()
)
}
// Componente pai: controla o estado
@Composable
fun FormularioDeCadastro() {
var nome by remember { mutableStateOf("") }
var email by remember { mutableStateOf("") }
Column(modifier = Modifier.padding(16.dp)) {
CampoDeNome(
nome = nome,
onNomeMudou = { nome = it }
)
}
}
Beneficios do state hoisting:
- O componente filho e reutilizavel e testavel.
- O pai tem controle total sobre o estado.
- O fluxo de dados e unidirecional: estado desce, eventos sobem.
derivedStateOf
Para estados que são derivados de outros estados:
@Composable
fun ListaComFiltro() {
var busca by remember { mutableStateOf("") }
val todosItens = remember { listOf("Kotlin", "Java", "Python", "Rust", "Go") }
// Recalcula apenas quando 'busca' ou 'todosItens' mudam
val itensFiltrados by remember(busca) {
derivedStateOf {
todosItens.filter { it.contains(busca, ignoreCase = true) }
}
}
Column {
TextField(value = busca, onValueChange = { busca = it })
itensFiltrados.forEach { item ->
Text(text = item)
}
}
}
derivedStateOf evita recomposicoes desnecessarias quando o calculo derivado produz o mesmo resultado.
Estado com ViewModel
Para estado que sobrevive a mudancas de configuração e representa lógica de negócio:
class ListaViewModel : ViewModel() {
private val _itens = MutableStateFlow<List<String>>(emptyList())
val itens: StateFlow<List<String>> = _itens.asStateFlow()
private val _carregando = MutableStateFlow(false)
val carregando: StateFlow<Boolean> = _carregando.asStateFlow()
fun carregar() {
viewModelScope.launch {
_carregando.value = true
_itens.value = repositorio.buscarItens()
_carregando.value = false
}
}
}
@Composable
fun ListaTela(viewModel: ListaViewModel = viewModel()) {
val itens by viewModel.itens.collectAsState()
val carregando by viewModel.carregando.collectAsState()
if (carregando) {
CircularProgressIndicator()
} else {
LazyColumn {
items(itens) { item -> Text(text = item) }
}
}
}
collectAsState() converte um StateFlow em State do Compose, integrando o ViewModel com a recomposicao.
Tipos de estado
// MutableState: estado simples do Compose
val estado: MutableState<Int> = mutableStateOf(0)
// StateFlow: estado observavel de coroutines
val fluxo: StateFlow<Int> = MutableStateFlow(0)
// LiveData: estado observavel do Android (legado)
val liveData: LiveData<Int> = MutableLiveData(0)
// Todos podem ser convertidos para State do Compose:
val a by remember { mutableStateOf(0) }
val b by fluxo.collectAsState()
val c by liveData.observeAsState(0)
snapshotFlow: State para Flow
Para observar mudancas de State como um Flow:
@Composable
fun BuscaComDebounce() {
var query by remember { mutableStateOf("") }
LaunchedEffect(Unit) {
snapshotFlow { query }
.debounce(300)
.distinctUntilChanged()
.collect { busca ->
// Executar busca apos 300ms sem digitacao
buscar(busca)
}
}
TextField(value = query, onValueChange = { query = it })
}
Quando usar cada tipo de estado
| Tipo | Uso | Escopo |
|---|---|---|
remember + mutableStateOf | Estado de UI local | Composable |
rememberSaveable | Estado que sobrevive rotação | Composable |
derivedStateOf | Estado calculado de outros estados | Composable |
ViewModel + StateFlow | Logica de negócio e dados | Tela |
snapshotFlow | Converter State em Flow | Bridge |
Casos de Uso no Mundo Real
Formularios reativos: em telas de cadastro, login ou checkout, cada campo (nome, email, senha, endereco) e representado por um
mutableStateOf. Mudancas no texto atualizam o estado, que por sua vez aciona recomposicao dos componentes visuais, incluindo validacao em tempo real e habilitacao/desabilitacao do botao de envio.Telas com carregamento e paginação: aplicações que consomem APIs usam
StateFlowno ViewModel para representar estados como carregando, sucesso, erro e lista vazia. O Composable coleta esse fluxo comcollectAsState()e renderiza a UI apropriada para cada estado, incluindo indicadores de progresso e mensagens de erro.Temas e preferencias do usuário: aplicações que permitem alternar entre tema claro e escuro ou alterar tamanho de fonte usam estado global (geralmente via
CompositionLocalou ViewModel compartilhado) para propagar mudancas de configuração por toda a arvore de Composables, recompondo apenas os componentes afetados.Jogos e animacoes interativas: aplicações que exibem animacoes baseadas em interação do usuário (arrastar, pincar, rotacionar) usam
mutableStateOfcombinado comAnimatablepara rastrear posicao, escala e rotação de elementos visuais, com recomposicao automatica a cada frame de animacao.
Boas Praticas
- Eleve o estado (state hoisting) sempre que um componente pai precisar controlar ou observar o valor: mantenha componentes filhos stateless, recebendo estado e callbacks como parametros. Isso torna os componentes reutilizaveis, testáveis e com fluxo de dados previsivel.
- Use
derivedStateOfpara calculos derivados de outros estados: em vez de recompor toda vez que um estado base muda,derivedStateOfrecalcula o valor derivado apenas quando necessário e só aciona recomposicao se o resultado realmente mudar. - Separe estado de UI de estado de negócio: estado de UI local (como se um dropdown esta aberto) deve ficar no Composable com
remember. Estado de negócio (como a lista de itens carregados do servidor) deve ficar no ViewModel comStateFlow. - Nunca mute colecoes dentro de
mutableStateOfdiretamente: o Compose detecta mudancas por referência. Alterar o conteudo de umaMutableListsem criar uma nova instancia não aciona recomposicao. Sempre crie uma nova colecao ao modificar. - Prefira
rememberSaveablepara estado que precisa sobreviver a rotação de tela:rememberperde o valor quando a Activity e recriada. Para dados como texto digitado pelo usuário, userememberSaveablepara preservar o estado automaticamente.
Perguntas Frequentes
P: Qual a diferenca entre mutableStateOf e MutableStateFlow?
R: mutableStateOf e a primitiva de estado do Compose, integrada diretamente ao sistema de recomposicao. MutableStateFlow e uma primitiva de coroutines que representa um fluxo de estado. No ViewModel, prefira StateFlow para expor estado (pois o ViewModel não deve depender do Compose). No Composable, converta para State com collectAsState().
P: Quando devo usar remember vs rememberSaveable vs ViewModel?
R: Use remember para estado de UI efemero que não precisa sobreviver a rotação (ex: se um tooltip esta visivel). Use rememberSaveable para estado de UI que deve sobreviver a mudancas de configuração (ex: texto digitado em um campo). Use ViewModel para estado de negócio e dados carregados de APIs ou banco de dados.
P: Por que meu Composable não recompoe quando mudo o estado?
R: As causas mais comuns sao: (1) esqueceu de usar remember, entao o estado e recriado a cada recomposicao; (2) esta mutando uma colecao existente em vez de criar uma nova; (3) o valor sendo comparado e estruturalmente igual ao anterior, entao o Compose não detecta mudanca; (4) o estado esta sendo lido fora do escopo de composicao (ex: em um LaunchedEffect sem dependência correta).
P: E seguro acessar mutableStateOf de várias threads?
R: As leituras e escritas individuais em mutableStateOf sao thread-safe. Porem, operações compostas (ler e escrever baseado no valor lido) não sao atomicas. Para esses casos, use Snapshot.withMutableSnapshot ou gerencie a concorrencia no ViewModel com coroutines.
Erros comuns
- Esquecer
remember: semremember, o estado e recriado a cada recomposicao, perdendo o valor.
// ERRADO
@Composable
fun Quebrado() {
var x = mutableStateOf(0) // Recriado toda recomposicao!
}
// CORRETO
@Composable
fun Correto() {
var x by remember { mutableStateOf(0) }
}
Colocar lógica de negócio no Composable: buscar dados, processar regras de negócio e acessar banco de dados devem ficar no ViewModel, não no Composable.
Nao elevar estado quando necessário: manter estado em componentes filhos quando o pai precisa controla-lo dificulta a comunicação e reutilização.
mutação direta de coleções: mudar o conteudo de uma
MutableListdentro de ummutableStateOfnão aciona recomposicao. Você precisa criar uma nova lista.
// ERRADO: nao aciona recomposicao
val lista = remember { mutableStateOf(mutableListOf(1, 2, 3)) }
lista.value.add(4) // Compose nao detecta!
// CORRETO: cria nova lista
val lista = remember { mutableStateOf(listOf(1, 2, 3)) }
lista.value = lista.value + 4 // Nova referência, Compose detecta
- Usar
rememberpara estado que precisa sobreviver a rotação:remembernão sobrevive a mudancas de configuração. UserememberSaveableou ViewModel.
Termos relacionados
- Composable: função que lê State e e recomposta quando o estado muda.
- Recomposicao: reexecucao de funções Composable quando seus estados de entrada mudam.
- ViewModel: gerencia estado e lógica de negócio fora da camada de UI.
- StateFlow: fluxo de estado de coroutines que pode ser convertido em State do Compose.
- remember: função que preserva valores entre recomposicoes.
- Flow: stream reativo que pode ser coletado como State usando
collectAsState.
State e o conceito central do Jetpack Compose. Entender como criar, elevar, derivar e gerenciar estado e o que separa interfaces reativas e corretas de interfaces com bugs de sincronizacao. O modelo declarativo do Compose torna o gerenciamento de estado mais previsivel e menos propenso a erros do que a abordagem imperativa tradicional.