Composition: essência, construção da árvore de UI no Compose

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

Composition é o processo central no Jetpack Compose, durante o qual uma árvore de UI viva exibida na tela é construída a partir de funções Composable descritivas. Ao contrário do sistema View do Android, onde os layouts eram carregados de XML e convertidos em objetos imutáveis, o Composition funciona como um sistema dinâmico: as funções executam, criam slots na memória, formam uma hierarquia de nós e a vinculam ao estado. De acordo com Google Android Developers, 2026, entender o Composition é crítico para otimizar o desempenho de aplicativos Compose.

Pontos principais

  • Composition é a execução de funções Composable para construir a árvore de UI
  • Slots são células de memória que armazenam parâmetros e estado de cada função
  • Posição no Compose (Positional Memorization) vincula o estado a um local no código
  • Primeira passagem Composition cria a árvore de UI inicial quando a tela é iniciada
  • CompositionLocal passa dados pela árvore sem parâmetros explícitos

O que é Composition no Jetpack Compose

Composition é o processo de execução de funções Composable, resultando em uma representação interna da interface do usuário como uma árvore de nós. Cada nó desta árvore corresponde a um componente integrado (Text, Button, Image) ou a uma chamada de função Composable definida pelo usuário. Composition não cria diretamente objetos View do Android — ele constrói uma descrição abstrata que é então processada pelas fases de Layout e Drawing.

A característica principal do Composition é sua reiniciabilidade. Cada função Composable dentro da composição pode ser reiniciada a qualquer momento se seus parâmetros de entrada ou os objetos de estado que ela lê foram alterados. O sistema não reinicia toda a árvore — apenas aquelas funções que realmente dependem dos dados alterados.

Tecnicamente, o Composition é gerenciado através do Composer — um mecanismo interno que o compilador Kotlin incorpora em cada função Composable. O Composer escreve em slots (grupos de posição) informações sobre quais funções foram chamadas, com quais parâmetros e em qual ordem. Em chamadas subsequentes, o Composer compara os novos dados com os dados armazenados e decide se deve reiniciar.

Como a árvore de UI é construída durante o Composition

O processo de construção da árvore de UI começa com a chamada do método setContent dentro de uma Activity ou Fragment. Este método cria o Composition inicial e começa a executar a função Composable raiz. Em seguida, cada função Composable aninhada adiciona seus nós à árvore, formando uma hierarquia: Row contém Text e Button, Column contém Image e Card, e assim por diante.

Cada nó da árvore recebe uma chave de posição única, baseada em sua posição no código fonte. Esta chave é usada para identificar o nó durante execuções subsequentes. A chave de posição é a razão pela qual a ordem de chamada das funções Composable não deve depender de condições: se em uma execução A -> B é chamado, e na próxima B -> A, o Compose não conseguirá corresponder os nós antigos e novos.

kotlin
@Composable
fun AppScreen() {
    Column {                     // Nó Column (posição 1)
        HeaderSection()            // Nó HeaderSection (posição 2)
        ContentSection()           // Nó ContentSection (posição 3)
        FooterSection()            // Nó FooterSection (posição 4)
    }
}

@Composable
fun HeaderSection() {
    Row {                       // Nó Row (posição 2.1)
        Text("Título")         // Nó Text (posição 2.2)
        Icon(...)                // Nó Icon (posição 2.3)
    }
}

Neste exemplo, cada chamada recebe uma posição baseada na ordem no código. Column (posição 1) contém três nós filhos (posições 2, 3, 4). HeaderSection adiciona mais dois nós filhos (2.1, 2.2, 2.3). Se na próxima recomposição ContentSection for chamado antes de HeaderSection, o Composer não conseguirá corresponder corretamente os nós — daí a regra: a ordem das chamadas de funções Composable deve ser estável.

Gerenciamento de estado no Composition

O estado no Composition é gerenciado através de objetos do tipo State<T>. Quando uma função Composable lê um valor do State através de uma propriedade delegada (by), ela registra uma dependência desse State. Quando o valor muda, todas as funções que leram este State são marcadas para reinício na próxima fase de composição.

O mecanismo de registro de dependências é chamado de sistema de snapshot. Cada vez que o State muda, um snapshot registra todas as alterações e notifica o Composer quais funções dependem desse State. É importante entender: ler State dentro de código não-Composable (por exemplo, em uma lambda onClick) não registra dependência — apenas a leitura dentro de uma função Composable ou em lambdas executadas no contexto da composição.

O sistema de snapshot funciona transacionalmente: múltiplas alterações de State dentro de um único evento são combinadas em uma transação, evitando múltiplas recomposições. Isso é especialmente importante ao lidar com gestos: um movimento altera vários objetos State, mas o Compose realiza apenas uma recomposição.

kotlin
@Composable
fun StateExample() {
    var text by remember { mutableStateOf("Hello") }
    var isVisible by remember { mutableStateOf(true) }

    Column {
        Text(text)  // registra dependência de text

        if (isVisible) {  // registra dependência de isVisible
            TextField(value = text, onValueChange = { text = it })
        }

        Button(onClick = { isVisible = !isVisible }) {
            Text(if (isVisible) "Ocultar" else "Mostrar")
        }
    }
}

Alterar o text aciona a recomposição apenas de Column, Text e TextField. Column, Button e a condição isVisible permanecem inalterados. Este isolamento da recomposição é uma vantagem chave do Compose sobre sistemas que redesenham a tela inteira. Cada função Composable rastreia apenas os objetos State que ela lê diretamente.

Composition vs Recomposition: diferenças principais

Composition e Recomposition são dois modos diferentes de executar funções Composable. Composition ocorre uma vez quando a tela é criada: o sistema executa todas as funções Composable com valores iniciais e constrói a árvore de UI inicial. Recomposition ocorre várias vezes quando os dados mudam: o sistema reinicia apenas as funções que dependem do estado alterado.

Modo Composition ativa todos os nós da árvore, aloca slots para cada função e registra todos os descendentes. Recomposition funciona seletivamente: o Compose compara os valores novos e antigos dos parâmetros de cada função, e se não mudaram — a função não é executada (skipping).

Composition e Recomposition diferem em custo. Primeiro Composition é mais caro porque requer a construção completa da árvore e alocação de slots. Recomposition é mais barato, especialmente se a maioria das funções é estável — seus parâmetros são comparados por equals, e o Compose pula sua invocação. Para máximo desempenho, você deve buscar que a maioria das recomposições afete o menor número possível de funções.

CaracterísticaCompositionRecomposition
Quando ocorreUma vez, na primeira exibiçãoVárias vezes, na alteração de dados
EscopoÁrvore inteiraApenas funções alteradas
Comparação de parâmetrosNão realizadaRealizada para skipping
Criação de slotsSim, todos os slots criadosApenas para novos nós

CompositionLocal: passando dados pela árvore

CompositionLocal é um mecanismo para passar dados implicitamente pela árvore de composição. Ele resolve o problema quando um parâmetro precisa ser passado através de dezenas de funções Composable aninhadas que não o usam diretamente. Em vez de uma cadeia explícita de parâmetros, os dados são definidos no nível superior e lidos em qualquer função aninhada através de CompositionLocal.current.

MaterialTheme é o exemplo mais conhecido de CompositionLocal. Todos os componentes do Compose leem cores, tipografia e formas através de MaterialTheme.colorScheme, MaterialTheme.typography, MaterialTheme.shapes, sem recebê-los através de parâmetros. Desenvolvedores podem criar seus próprios CompositionLocal para dados como o usuário atual, configurações de localização ou configuração de tela.

Uma limitação importante: CompositionLocal não deve ser usado para dados que mudam frequentemente (posição de rolagem, texto em um campo de entrada). Um componente que lê CompositionLocal reinicia toda vez que o valor muda, então para dados dinâmicos é melhor usar parâmetros explícitos ou State. CompositionLocal é ideal para dados de configuração que mudam raramente ou nunca.

kotlin
val LocalUser = compositionLocalOf<User?> { null }

@Composable
fun AppRoot(user: User, content: @Composable () -> Unit) {
    CompositionLocalProvider(LocalUser.provides(user)) {
        content()
    }
}

@Composable
fun UserAvatar() {
    val user = LocalUser.current  // leitura sem parâmetro explícito
    AsyncImage(model = user?.avatarUrl, contentDescription = "Avatar")
}

CompositionLocalProvider cria um escopo dentro do qual LocalUser.current retorna o valor especificado. UserAvatar lê o usuário sem passar explicitamente o parâmetro através de funções intermediárias. Isso é especialmente valioso em hierarquias profundas onde os dados são necessários apenas em alguns nós folha.

Perguntas frequentes

O que acontece se o State for alterado durante o Composition

Alterar o State durante o Composition agenda uma nova recomposição, que será executada após a conclusão da atual. Não ocorre loop infinito: o Compose garante que cada recomposição é realizada em uma transação separada do sistema de snapshot.

Quanto tempo leva o Composition de uma tela complexa

Em dispositivos modernos, o Composition de uma tela com 50–100 funções Composable leva 1–5 ms. O Google recomenda permanecer dentro de 16 ms para um quadro de 60fps. Se o Composition exceder este limite, use LazyColumn ou divida a tela em funções menores.

Pode-se iniciar o Composition manualmente

O início manual direto do Composition não é possível — ele é gerenciado pelo Composer automaticamente. No entanto, você pode forçar uma recomposição alterando o State ou chamando invalidate() no composable raiz se tiver acesso ao CompositionContext.

Como o Composition difere da hierarquia View no Android clássico

A hierarquia View é uma árvore imutável de objetos Java que é criada uma vez. Composition é uma árvore virtual que é reconstruída cada vez que os dados mudam. View armazena seu estado em variáveis de instância, Composition — em slots vinculados à posição da chamada da função.

Como o Composition lida com a remoção de nós

Se uma função Composable não for mais chamada (por exemplo, uma condição if se tornar false), o Composition remove seu nó e aciona a limpeza do DisposableEffect. Quando ela reaparece (if se torna true novamente), um novo nó é criado — o antigo não é restaurado.

Resumo

  • Composition é o processo de executar funções Composable para construir uma árvore de UI vinculada ao estado
  • Composer gerencia slots, registra chamadas de funções e compara parâmetros durante a recomposição
  • Sistema de snapshot registra dependências de funções do State e combina alterações em transações
  • Composition executa uma vez na inicialização, Recomposition — na alteração de dados
  • CompositionLocal passa dados de configuração pela árvore sem uma cadeia explícita de parâmetros
  • Posição de uma chamada de função serve como seu identificador único na árvore de composição
  • Recomendação: mantenha funções Composable pequenas com parâmetros imutáveis para skipping eficiente

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