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 (sem remember, 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

TipoUsoEscopo
remember + mutableStateOfEstado de UI localComposable
rememberSaveableEstado que sobrevive rotacaoComposable
derivedStateOfEstado calculado de outros estadosComposable
ViewModel + StateFlowLogica de negocio e dadosTela
snapshotFlowConverter State em FlowBridge

Erros comuns

  1. Esquecer remember: sem remember, 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) }
}
  1. Colocar logica de negocio no Composable: buscar dados, processar regras de negocio e acessar banco de dados devem ficar no ViewModel, nao no Composable.

  2. Nao elevar estado quando necessario: manter estado em componentes filhos quando o pai precisa controla-lo dificulta a comunicacao e reutilizacao.

  3. Mutacao direta de colecoes: mudar o conteudo de uma MutableList dentro de um mutableStateOf nao 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
  1. Usar remember para estado que precisa sobreviver a rotacao: remember nao sobrevive a mudancas de configuracao. Use rememberSaveable ou 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.