O que e Composable em Kotlin?

Uma funcao Composable e uma funcao anotada com @Composable que descreve parte da interface do usuario no Jetpack Compose, o toolkit moderno de UI declarativa do Android. Em vez de montar telas com XML e manipular Views programaticamente, voce escreve funcoes Kotlin que descrevem como a UI deve parecer para um dado estado.

O Compose e inspirado em frameworks como React e Flutter, mas aproveita todo o poder do Kotlin: type safety, extension functions, coroutines e uma sintaxe concisa que faz a construcao de interfaces parecer natural.

Sintaxe basica

import androidx.compose.runtime.Composable
import androidx.compose.material3.Text

@Composable
fun Saudacao(nome: String) {
    Text(text = "Ola, $nome!")
}

A anotacao @Composable informa ao compilador do Compose que essa funcao descreve UI e pode ser recomposta (re-executada) quando os dados mudam. Funcoes Composable so podem ser chamadas dentro de outros contextos Composable.

Composicao de funcoes

O poder do Compose esta na composicao. Voce cria componentes pequenos e os combina para formar telas completas:

@Composable
fun CartaoDeUsuario(nome: String, email: String) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        Text(
            text = nome,
            style = MaterialTheme.typography.headlineSmall
        )
        Spacer(modifier = Modifier.height(4.dp))
        Text(
            text = email,
            style = MaterialTheme.typography.bodyMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant
        )
    }
}

@Composable
fun ListaDeUsuarios(usuarios: List<Usuario>) {
    LazyColumn {
        items(usuarios) { usuario ->
            CartaoDeUsuario(
                nome = usuario.nome,
                email = usuario.email
            )
        }
    }
}

Cada funcao Composable e um bloco reutilizavel. CartaoDeUsuario nao sabe onde sera usado, e ListaDeUsuarios nao sabe como o cartao e renderizado internamente. Essa separacao torna o codigo modular e testavel.

Estado em Composables

Funcoes Composable sao reexecutadas quando o estado muda. Para gerenciar estado local, use remember e mutableStateOf:

@Composable
fun Contador() {
    var contagem by remember { mutableStateOf(0) }

    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text(text = "Contagem: $contagem")
        Spacer(modifier = Modifier.height(8.dp))
        Button(onClick = { contagem++ }) {
            Text("Incrementar")
        }
    }
}

Quando contagem muda, o Compose recompoe apenas as partes da UI que dependem desse valor. Isso e feito de forma inteligente pelo runtime do Compose, que rastreia quais Composables leem quais estados.

Recomposicao

Recomposicao e o processo pelo qual o Compose reexecuta funcoes Composable quando os dados de entrada mudam. E importante entender que:

  • A recomposicao pode acontecer a qualquer momento e em qualquer ordem.
  • Funcoes Composable devem ser idempotentes: chamar com os mesmos parametros deve produzir o mesmo resultado.
  • Funcoes Composable nao devem ter efeitos colaterais nao controlados.
// Correto: sem efeitos colaterais
@Composable
fun ExibirNome(nome: String) {
    Text(text = nome)
}

// Incorreto: efeito colateral na recomposicao
@Composable
fun ExibirNomeComLog(nome: String) {
    println("Recompondo...") // Pode ser chamado muitas vezes!
    Text(text = nome)
}

Para efeitos colaterais controlados, use APIs como LaunchedEffect, SideEffect e DisposableEffect.

Efeitos colaterais controlados

@Composable
fun TelaDeDetalhes(userId: String) {
    var usuario by remember { mutableStateOf<Usuario?>(null) }

    LaunchedEffect(userId) {
        // Executa quando userId muda
        usuario = repositorio.buscarUsuario(userId)
    }

    usuario?.let { u ->
        Column {
            Text(text = u.nome)
            Text(text = u.email)
        }
    }
}

LaunchedEffect lanca uma coroutine que e cancelada e reiniciada quando a chave (userId) muda. Isso garante que efeitos colaterais sejam gerenciados corretamente no ciclo de vida do Composable.

Modifiers

Modifiers sao o mecanismo do Compose para decorar e configurar componentes:

@Composable
fun CartaoEstilizado() {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
            .clickable { /* acao */ }
    ) {
        Text(
            text = "Conteudo do cartao",
            modifier = Modifier.padding(24.dp)
        )
    }
}

A ordem dos modifiers importa: padding antes de clickable produz resultado diferente de clickable antes de padding.

Quando usar Composables

Composables sao usados em qualquer projeto Android que adote Jetpack Compose:

  • Telas de aplicativos: construir layouts completos de forma declarativa.
  • Componentes reutilizaveis: criar uma biblioteca de componentes de UI consistentes.
  • Previews: usar @Preview para visualizar componentes no Android Studio sem rodar o app.
  • Testes de UI: Composables sao facilmente testaveis com a biblioteca de testes do Compose.
  • Compose Multiplatform: o mesmo conceito funciona em desktop, web e iOS com Kotlin Multiplatform.

Erros comuns

  1. Colocar logica de negocio dentro do Composable: Composables devem apenas descrever UI. Logica de negocio pertence ao ViewModel ou a camadas inferiores.

  2. Esquecer remember: sem remember, o estado e recriado a cada recomposicao, perdendo o valor anterior.

// Errado: contagem volta a 0 a cada recomposicao
@Composable
fun ContadorQuebrado() {
    var contagem = mutableStateOf(0) // Falta remember!
    // ...
}
  1. Efeitos colaterais descontrolados: fazer chamadas de rede, acessar banco de dados ou modificar estado global diretamente dentro de um Composable sem usar LaunchedEffect ou similar.

  2. Nao elevar o estado (state hoisting): manter estado em componentes filhos quando ele deveria ser controlado pelo pai. Isso dificulta reutilizacao e testes.

  3. Ignorar a ordem dos Modifiers: como modifiers sao aplicados em cadeia, a ordem afeta o resultado visual. Testar e visualizar com @Preview ajuda a pegar esses problemas cedo.

State hoisting

Um padrao fundamental em Compose e elevar o estado para o componente pai:

// Componente stateless (sem estado proprio)
@Composable
fun CampoDeTexto(
    valor: String,
    onValorMudou: (String) -> Unit
) {
    TextField(
        value = valor,
        onValueChange = onValorMudou
    )
}

// Componente pai controla o estado
@Composable
fun Formulario() {
    var nome by remember { mutableStateOf("") }
    CampoDeTexto(
        valor = nome,
        onValorMudou = { nome = it }
    )
}

Isso torna CampoDeTexto reutilizavel e testavel, pois ele nao gerencia seu proprio estado.

Termos relacionados

  • State: o mecanismo de estado reativo que aciona recomposicoes quando valores mudam.
  • Modifier: objeto que configura aparencia e comportamento de componentes Compose.
  • ViewModel: gerencia estado e logica de negocio fora da camada de UI.
  • LaunchedEffect: API para executar efeitos colaterais controlados dentro de Composables.
  • Recomposicao: processo de reexecucao de funcoes Composable quando seus inputs mudam.
  • remember: funcao que preserva estado entre recomposicoes.

Composables representam uma mudanca de paradigma na construcao de interfaces em Kotlin. Ao tratar a UI como funcao do estado, o Compose elimina toda uma classe de bugs relacionados a sincronizacao entre dados e interface, tornando o desenvolvimento mais previsivel e produtivo.