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 é 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.
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.
@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.
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.
@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 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ística | Composition | Recomposition |
|---|---|---|
| Quando ocorre | Uma vez, na primeira exibição | Várias vezes, na alteração de dados |
| Escopo | Árvore inteira | Apenas funções alteradas |
| Comparação de parâmetros | Não realizada | Realizada para skipping |
| Criação de slots | Sim, todos os slots criados | Apenas para novos nós |
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.
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
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.
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.
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.
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.
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
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.
Leia também