O que e State em Kotlin?
No contexto do Jetpack Compose, State (estado) e um valor que, quando muda, aciona automaticamente a recomposicao das funcoes 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 estatica. Com State, voce declara como a UI deve parecer para cada possivel valor do estado, e 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: delegacao 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 rotacao de tela ou a recriacao da Activity.
State hoisting (elevacao de estado)
O padrao 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 sao 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 configuracao e representa logica de negocio:
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 rotacao | Composable |
derivedStateOf | Estado calculado de outros estados | Composable |
ViewModel + StateFlow | Logica de negocio e dados | Tela |
snapshotFlow | Converter State em Flow | Bridge |
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 logica de negocio no Composable: buscar dados, processar regras de negocio e acessar banco de dados devem ficar no ViewModel, nao no Composable.
Nao elevar estado quando necessario: manter estado em componentes filhos quando o pai precisa controla-lo dificulta a comunicacao e reutilizacao.
Mutacao direta de colecoes: mudar o conteudo de uma
MutableListdentro de ummutableStateOfnao aciona recomposicao. Voce 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 referencia, Compose detecta
- Usar
rememberpara estado que precisa sobreviver a rotacao:remembernao sobrevive a mudancas de configuracao. UserememberSaveableou ViewModel.
Termos relacionados
- Composable: funcao que le State e e recomposta quando o estado muda.
- Recomposicao: reexecucao de funcoes Composable quando seus estados de entrada mudam.
- ViewModel: gerencia estado e logica de negocio fora da camada de UI.
- StateFlow: fluxo de estado de coroutines que pode ser convertido em State do Compose.
- remember: funcao 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.