Recomposition — o que é, reconstrução da UI ao mudar o estado

Autor: IT Sectr Publicado: 2026-06-27 Tempo de leitura: 7 min

Recomposition é um mecanismo do Jetpack Compose que reconstrói automaticamente partes da interface do usuário quando os dados mudam, sem atualização manual de elementos View. Quando uma variável de estado da qual uma função Composable depende muda seu valor, o Compose reinicia apenas essa função, deixando o resto da árvore UI intocada. De acordo com Google Android Developers, 2026, o entendimento correto do Recomposition permite reduzir redesenhos desnecessários em 40–60%.

Pontos Principais

  • Recomposition — reinicialização de funções Composable quando seus dados de entrada ou Estado mudam
  • Skipping — pulo de funções cujos parâmetros não mudaram (comparação por equals)
  • Stability determina se o Compose pode pular uma função — tipos estáveis são comparados corretamente
  • Smart Recomposition reinicia apenas o conjunto mínimo de funções, não a árvore inteira
  • Recomposição não garante redesenho — Layout e Drawing podem pular sua fase

O que é Recomposition no Jetpack Compose

Recomposition é a reexecução de funções Composable que já participaram da Composition, com novos valores de parâmetros ou estado. O principal objetivo da recomposição é sincronizar a árvore UI com os dados atuais sem reconstruir toda a interface do zero. Ao contrário da Composition, que ocorre uma vez, a Recomposition pode ser acionada centenas de vezes durante a vida útil de uma tela.

Recomposition funciona no princípio de smart invalidation: o Compose rastreia quais objetos State cada função Composable lê e marca para reinicialização apenas aquelas cujas dependências mudaram. Isso é alcançado através de um sistema de snapshot que registra todas as operações de leitura de State durante a execução, e um Composer que mapeia essas dependências para funções específicas.

É importante entender: recomposição não significa redesenho imediato da tela. O Compose trabalha em três fases: Composition (construção da descrição UI), Layout (cálculo de tamanhos e posições) e Drawing (renderização no canvas). Se após a recomposição os tamanhos e posições dos elementos não mudaram, a fase Layout pode ser pulada. Se a aparência visual não mudou — Drawing é pulado. Essa arquitetura de três fases garante custo mínimo para cada atualização de UI.

Gatilhos de recomposição: o que causa reinicialização de funções

Existem três gatilhos principais de recomposição. O primeiro é uma mudança em um objeto State lido dentro do corpo de uma função Composable. Quando mutableStateOf ou derivedStateOf muda seu valor, todas as funções que registraram a leitura deste State na composição anterior são marcadas para reinicialização.

O segundo gatilho é uma mudança de parâmetro de uma função Composable quando chamada de uma função pai. Se a função pai passa um novo valor (por exemplo, o texto ou número mudou), a função filha será reiniciada, mesmo que não leia State internamente. O Compose compara valores novos e antigos dos parâmetros via equals, e se forem iguais — a função pode ser pulada.

O terceiro gatilho é uma mudança de CompositionLocal via CompositionLocalProvider. Todas as funções que leem CompositionLocal através de .current são reiniciadas quando o provedor muda. Este mecanismo é usado pelo MaterialTheme: mudar o tema (claro/escuro) causa recomposição de todos os componentes que leem MaterialTheme.colorScheme.

kotlin
@Composable
fun RecompositionDemo() {
    var counter by remember { mutableStateOf(0) }
    var text by remember { mutableStateOf("Hello") }

    Column {
        Text("Counter: $counter")  // recomposition when counter changes
        Text("Message: $text")    // recomposition when text changes

        Button(onClick = { counter++ }) {
            Text("+1")
        }
        Button(onClick = { text = "World" }) {
            Text("Change Text")
        }
    }
}

Clicar no botão +1 muda counter, causando recomposição apenas da primeira linha Text e da própria Column. A segunda linha Text exibindo text não reinicia. Este isolamento é resultado do sistema de snapshot: cada função Composable só conhece os objetos State que leu.

Otimização da recomposição: técnicas práticas

A otimização da recomposição começa com a escolha das estruturas de dados corretas. Use coleções imutáveis (listOf, mapOf) em vez de mutáveis (mutableListOf). O Compose compara parâmetros via equals, e se uma coleção mudou mas equals retornou true — a função não será reiniciada. Para coleções mutáveis, use SnapshotStateList, que implementa rastreamento correto de mudanças no nível do elemento.

A segunda técnica é extrair partes estáveis da UI em funções Composable separadas. Se parte de uma tela não depende de estado que muda frequentemente, extraia-a em uma função separada com parâmetros. Quando a recomposição ocorre, a função estável recebe os mesmos parâmetros, o Compose os compara e pula a execução. Isso é mais eficiente do que reiniciar essa parte como parte de uma função grande onde alguns parâmetros mudaram.

A terceira técnica são chaves em LazyColumn. Sempre especifique uma chave para itens em LazyColumn, LazyGrid e outros contêineres lentos. A chave permite ao Compose identificar elementos quando a lista muda: adicionando, removendo ou reordenando. Sem uma chave, o Compose reinicia todos os itens da lista em qualquer mudança, o que em listas grandes causa degradação notável de desempenho.

kotlin
// Optimized structure: stable parts extracted separately
@Composable
fun OptimizedScreen(items: List<Item>) {
    Column {
        Header()                         // does not depend on items — no recomposition
        Spacer(modifier = Modifier.height(8.dp))
        LazyColumn {
            items(items, key = { it.id }) { item ->
                ItemRow(item = item)   // recomposition only for changed items
            }
        }
    }
}

@Composable
fun Header() {
    Text("Item list", style = MaterialTheme.typography.headlineMedium)
}

@Composable
fun ItemRow(item: Item) {
    Text(item.title)
}

Skipping e Stability no Compose

Skipping é um mecanismo onde o Compose pula a execução de uma função Composable se todos os seus parâmetros não mudaram. Para o skipping funcionar corretamente, os tipos dos parâmetros devem ser estáveis (stable). O compilador Kotlin marca como estáveis: tipos primitivos (Int, Float, Boolean), String, funções lambda, e classes cujos todos os campos são estáveis e val.

Stability é a anotação @Stable ou @Immutable que pode ser adicionada a classes de dados personalizadas. Se uma classe contém um campo mutável (var), o compilador a considera instável, e o Compose não poderá pular funções com tais parâmetros. Para classes com var, use @Stable se você garante que a notificação de mudança será enviada através do sistema de snapshot.

Você pode verificar a estabilidade usando a flag do compilador -P "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=. /reports". Ela gera um relatório com uma lista de todas as funções Composable e seus parâmetros indicando estabilidade. Se um parâmetro é instável — skipping é impossível para essa função, e ela será reiniciada em cada recomposição do pai.

TipoEstabilidadeSkipping
Int, Float, BooleanEstávelSim
StringEstávelSim
LambdaEstávelSim
data class com campos valEstávelSim
data class com campos varInstávelNão
List<String>InstávelNão

Nota: List<String> é considerada instável porque é uma interface, não uma implementação concreta. Use immutableListOf() da biblioteca Kotlin Collections Immutable ou envolva a lista em uma classe @Stable. Lambda é sempre estável porque seu equals compara apenas referências, e quando uma nova lambda é criada no local da chamada, a função pai também reinicia.

Monitoramento de recomposição no Android Studio

Para monitorar a recomposição, o Android Studio fornece o Layout Inspector com o modo Compose Recomposition Counts. Neste modo, cada função Composable exibe o número de recomposições e as razões de reinicialização. Isso permite encontrar rapidamente funções que recompoem com muita frequência e determinar a causa raiz — parâmetros instáveis ou dependências de State desnecessárias.

Ferramentas adicionais: Compose Metrics (coleta de estatísticas através de testes de instrumentação) e Recomposition Timer (medição do tempo de execução de cada função). O Google recomenda habilitar estas ferramentas durante a criação de perfil e desabilitá-las em versões de lançamento, pois adicionam até 20% de sobrecarga por recomposição.

Ao analisar recomposições, procure padrões de recomposição desnecessária: uma função reinicia mesmo que sua UI de saída não deva mudar. Uma causa comum é o uso de lambdas sem remember, onde um novo objeto lambda é criado a cada vez e o Compose considera o parâmetro alterado. Solução: envolva lambdas em remember { } com capturas fixas.

kotlin
// Bad: new lambda on every parent recomposition
@Composable
fun Parent() {
    Child(onClick = { doSomething() })  // new lambda every time
}

// Good: remember stabilizes the lambda
@Composable
fun Parent() {
    val onClick = remember { { doSomething() } }
    Child(onClick = onClick)  // same reference
}

Perguntas Frequentes

Recomposição significa redesenho da tela?

Não, a recomposição é apenas a fase de Composition. Depois dela, Layout e Drawing executam. Se após a recomposição os tamanhos e posições dos elementos não mudaram, Layout e Drawing podem ser completamente pulados, economizando recursos da GPU.

Com que frequência a recomposição pode ocorrer?

Durante animações, a recomposição pode rodar até 120 vezes por segundo (120fps). Para interação normal — 10–60 vezes por segundo. É importante que cada recomposição caiba no orçamento do quadro (8–16 ms), caso contrário o aplicativo irá lagar.

Por que uma função recompoe mesmo que o State não mudou?

A razão é uma mudança de parâmetro da função pai. O pai reinicia (por sua própria razão) e passa um novo valor. Para evitar isso, verifique a estabilidade dos parâmetros e use remember para estabilizar lambdas e valores calculados.

Pode-se desabilitar a recomposição para uma função específica?

Não há desabilitação direta, mas existe o skipping forçado via readInComposition — State é lido fora do corpo da função, o que não registra uma dependência. Use com cuidado: a função não reagirá a mudanças, o que pode levar a UI obsoleta.

O que é mais caro: Composition ou Recomposition?

Composition é mais cara porque cria todos os slots e nós da árvore do zero. Recomposition reutiliza slots existentes e apenas atualiza seus valores. Na prática, Composition de uma tela leva 2–10 ms, enquanto a recomposição de um único elemento leva 0.1–1 ms.

Resumo

  • Recomposition — reinicialização seletiva de funções Composable quando seu Estado ou parâmetros mudam
  • Sistema de snapshot rastreia dependências de funções do Estado e agenda recomposição
  • Skipping só é possível para funções com parâmetros estáveis (@Stable ou immutable)
  • Três gatilhos de recomposição: mudança de Estado, mudança de parâmetros, mudança de CompositionLocal
  • List<T> é considerada instável — use coleções imutáveis para skipping correto
  • Layout Inspector mostra contadores de recomposição para cada função
  • Recomendação: extraia partes estáveis da UI em funções separadas e use remember para lambdas

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.

Discutir o projeto

Leia também