composable(): o que é, NavHost e roteamento no Jetpack Compose

Autor: IT Sectr Publicado: 2026-06-30 Tempo de leitura: 9 min

composable() é uma função da biblioteca Navigation Compose que registra uma tela no NavHost e conecta uma rota URL ao layout Compose. Quando a navegação vai para uma rota definida, o Jetpack Compose chama a função composable correspondente e a exibe como a tela atual. Ao contrário do FragmentManager ou da navegação baseada em Intent, o composable() funciona no nível de uma única Activity e é totalmente gerenciado via Kotlin DSL. De acordo com Android Developers (2025), mais de 73% dos aplicativos Android modernos construídos com Jetpack Compose usam o Navigation Compose para organizar as transições de tela.

Principais pontos

  • composable() é uma função para registrar uma tela no NavHost da biblioteca Navigation Compose.
  • Rota — cada tela é identificada por uma rota de string passada como primeiro argumento.
  • Parâmetros — composable() suporta argumentos via NavArgument, incluindo obrigatórios e opcionais.
  • Aninhamento — a navegação aninhada é suportada através de NavHost aninhados com grafos de rotas separados.
  • Desempenho — composable() usa inicialização preguiçosa: a tela é criada apenas na primeira navegação.

O que é composable() no NavHost

composable() é uma função de extensão do objeto NavHost. O Kotlin DSL permite chamá-la dentro do bloco NavHost para descrever declarativamente todas as telas do aplicativo. Cada chamada cria uma entrada no grafo de navegação, vinculando uma rota de string a uma função composable. Quando um usuário navega para uma rota específica, o NavHost exibe o composable correspondente como a tela atual, ocultando a anterior.

A biblioteca Navigation Compose foi apresentada pelo Google em 2021 como uma alternativa à navegação baseada em Fragment para Jetpack Compose. A principal vantagem é a compatibilidade total com o paradigma Compose: composable() funciona no mesmo ciclo de vida que outros componentes Compose, sem necessidade de FragmentManager ou transações. Isso elimina uma classe de bugs relacionados à incompatibilidade de ciclo de vida entre Fragment e Compose.

Cada composable() recebe uma rota de string e uma função lambda que recebe um objeto NavBackStackEntry e retorna uma UI Composable. Dentro da lambda, pode-se acessar o NavController através de navController do escopo, permitindo a navegação para outras telas. Essa arquitetura torna a navegação explícita e previsível.

kotlin
@Composable
fun AppNavigation() {
    val navController = rememberNavController()
    
    NavHost(
        navController = navController,
        startDestination = "home"
    ) {
        composable("home") {
            HomeScreen(
                onNavigateToProfile = {
                    navController.navigate("profile")
                }
            )
        }
        composable("profile") {
            ProfileScreen(
                onBack = { navController.popBackStack() }
            )
        }
    }
}

Como composable() funciona: chaves e parâmetros

Cada chamada a composable() cria um vértice com um identificador de rota único no grafo interno do NavHost. Quando o NavController executa navigate(), a biblioteca compara a rota solicitada com todos os vértices composable registrados e encontra uma correspondência. Após a correspondência, um NavBackStackEntry é criado, colocado na pilha de navegação e a composição da UI é iniciada.

A implementação interna do composable() usa um mecanismo de inicialização preguiçosa: a composição da tela ocorre apenas na primeira navegação para essa rota. Isso significa que as telas para as quais o usuário nunca navegou não ocupam memória nem executam nenhum código. Essa abordagem melhora significativamente o desempenho em aplicativos com muitas telas.

O parâmetro key no composable() permite gerenciar a recriação da tela. Por padrão, o composable não é recriado na navegação repetida para a mesma rota — o NavHost usa a entrada existente na pilha. No entanto, se uma key for passada e ela mudar, o NavHost criará uma nova instância da função composable. Isso é útil para telas com dados dinâmicos onde é necessário forçar a atualização do estado ao reabrir.

kotlin
val NavGraphBuilder.Composable: Unit
    get() = composable(
        route = "details/{itemId}",
        arguments = listOf(
            NavArgument("itemId") { 
                type = NavType.IntType
            }
        ),
        deepLinks = listOf(
            navDeepLink { uriPattern = "myapp://details/{itemId}" }
        )
    ) { backStackEntry ->
        val itemId = backStackEntry.arguments?.getInt("itemId") ?: 0
        DetailsScreen(itemId = itemId)
    }

Passar argumentos via composable()

composable() suporta um sistema flexível de argumentos através do parâmetro arguments. Cada argumento é descrito por um objeto NavArgument que define o tipo, o valor padrão e a obrigatoriedade. Os argumentos são passados na rota como parâmetros de caminho (via chaves) ou parâmetros de consulta (via ponto de interrogação).

Os parâmetros de caminho são especificados diretamente no modelo da rota: "profile/{userId}". Ao navegar para "profile/42", o NavHost extrai automaticamente o valor 42 e o torna acessível através de backStackEntry.arguments. Os parâmetros de consulta são adicionados após o ponto de interrogação: "search?query={text}" e também são analisados automaticamente pela biblioteca.

Ao extrair argumentos, é importante verificar a obrigatoriedade do parâmetro através de NavType.isNullableAllowed e fornecer valores padrão através de NavArgument defaultValue. Se um parâmetro obrigatório estiver ausente, o Navigation Compose lança uma exceção IllegalArgumentException, evitando bugs sutis com rotas incorretas.

Tipo de argumentoNavTypeExemplo na rota
IntNavType.IntType"item/{id}"
StringNavType.StringType"user/{name}"
BooleanNavType.BoolType"filter?enabled={value}"
FloatNavType.FloatType"map/{lat}/{lon}"
LongNavType.LongType"article/{timestamp}"

Para passar objetos complexos, recomenda-se usar NavType.ParcelableType ou NavType.SerializableType. No entanto, o Google aconselha minimizar o tamanho dos dados transferidos — é melhor passar um identificador e carregar o objeto por ID dentro da tela. Isso evita problemas com grandes dados serializados e simplifica o tratamento de mudanças de configuração.

kotlin
data class Profile(val id: Int, val name: String) : Parcelable

            // Navegar com dados mínimos
navController.navigate("profile/42")

            // Recuperar argumentos na tela
composable(
    route = "profile/{userId}",
    arguments = listOf(
        NavArgument("userId") { type = NavType.IntType }
    )
) { backStackEntry ->
    val userId = backStackEntry.arguments?.getInt("userId") ?: 0
    ProfileDetailScreen(userId = userId)
}

Navegação aninhada com composable()

Em aplicativos reais, é frequentemente necessário organizar grafos de navegação aninhados — por exemplo, uma pilha de telas separada dentro de uma aba BottomNavigation. composable() suporta aninhamento através de NavHost aninhados: dentro de uma tela composable, você pode declarar seu próprio NavHost com uma pilha de rotas independente.

Cada NavHost aninhado tem seu próprio NavController e back stack. Isso significa que a navegação dentro de uma aba não afeta a navegação em outras abas — o usuário pode alternar livremente entre as abas sem perder o histórico de navegação dentro de cada uma. Essa arquitetura é chamada de Scoped Navigation e é recomendada pelo Google para aplicativos com navegação complexa de vários níveis.

Ao implementar navegação aninhada, é importante gerenciar corretamente o estado do NavController: cada NavHost aninhado deve armazenar seu próprio rememberNavController dentro do escopo da função composable. De acordo com o Android Developer Summit 2024, mais de 40% dos aplicativos Jetpack Compose com três ou mais abas usam a arquitetura NavHost aninhada para isolar a navegação entre módulos.

kotlin
// NavHost principal com abas
composable("tabs") {
    MainTabsScreen { tab ->
        when (tab) {
            Tab.Home -> HomeNavGraph()
            Tab.Search -> SearchNavGraph()
        }
    }
}

// Grafo aninhado dentro da aba Início
@Composable
fun HomeNavGraph() {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        startDestination = "home_feed"
    ) {
        composable("home_feed") { FeedScreen() }
        composable("home_detail/{postId}") { PostDetailScreen() }
    }
}

composable() vs navegação com Intent

Antes do Jetpack Compose, a forma padrão de navegação no Android usava Intent e FragmentManager. Intent é uma mensagem do sistema que inicia uma nova Activity, o que implica recriar toda a árvore de View. Em contraste, composable() funciona dentro de uma única Activity e simplesmente substitui parte da árvore Compose, o que é significativamente mais rápido e eficiente em memória.

Principais diferenças entre composable() e navegação baseada em Intent:

  • Velocidade — composable() alterna telas em milissegundos sem recriar a Activity; Intent requer reiniciar a Activity.
  • Animações — no Navigation Compose, as animações de transição são definidas declarativamente através do AnimatedNavHost, sem necessidade de overridePendingTransition.
  • Estado compartilhado — composable() funciona em um escopo ViewModel compartilhado, simplificando a transferência de dados entre telas sem Intent extras.
Característicacomposable()Intent / Fragment
ArquiteturaSingle Activity, árvore ComposeMulti Activity, pilhas Fragment
Transferência de dadosparâmetros path/query, ViewModel compartilhadoIntent extras, Bundle, SharedPreferences
Links profundosSuporte integrado navDeepLinkintent-filter no manifesto
Back stackGerenciamento automático popBackStackFragmentManager.popBackStack()
Tempo de troca5–15 ms (dentro do processo)50–200 ms (com recriação)

Migrar de Intent para composable() não é apenas uma substituição de API, mas uma mudança de paradigma arquitetônico. Em vez de especificar explicitamente qual Activity deve abrir, o desenvolvedor descreve declarativamente todas as rotas possíveis em um só lugar, melhorando a legibilidade do código e simplificando os testes de navegação. De acordo com o Google I/O 2024, o Jetpack Compose com Navigation Compose reduz o código de navegação em 40–60% em comparação com o FragmentManager.

Erros comuns com composable()

Um dos erros mais comuns é recriar o NavController durante a recomposição. Se o NavController for criado via rememberNavController() no nível do composable pai, que pode ser recriado com a mudança de estado, a navegação quebra — o histórico é perdido. A solução correta é elevar o NavController a um nível de composable estável, como o nível da Activity ou o composable raiz do aplicativo.

O segundo problema comum é a recomposição infinita durante a navegação. Isso acontece quando navController.navigate() é colocado diretamente no corpo de uma função composable. Como a navegação altera o estado do NavHost, isso desencadeia uma recomposição, que chama navigate() novamente, criando um loop. Todas as chamadas de navegação devem ser encapsuladas em lambdas de manipuladores (onClick, onButtonPressed), não executadas na composição.

O terceiro erro é o manuseio incorreto do back stack ao usar BottomNavigation. A navegação simples via navigate() em cada troca de aba adiciona uma nova entrada à pilha em vez de retornar à existente. Para BottomNavigation, você deve usar navController.navigate() com restoreState = true e launchSingleTop = true, o que garante a restauração correta do estado ao alternar abas.

kotlin
fun NavController.navigateToTab(route: String) {
    navigate(route) {
        popUpTo(navController.graph.findStartDestination().id) {
            saveState = true
        }
        launchSingleTop = true
        restoreState = true
    }
}

Perguntas Frequentes

Qual é a diferença entre composable() e uma função @Composable normal?

composable() não é uma anotação, mas uma função de extensão do NavHost que vincula uma rota à UI. Uma função @Composable normal simplesmente descreve o layout, enquanto composable() registra esse layout no grafo de navegação com uma rota especificada, tornando-o acessível para navegação através do NavController.

Como passar um objeto complexo entre telas composable()?

Recomenda-se passar apenas um identificador (ID) através do parâmetro de caminho, e carregar o objeto na tela por ID através de um repositório ou ViewModel. Se ainda precisar passar o objeto, use NavType.ParcelableType, mas evite passar objetos maiores que 1 KB — isso pode levar a TransactionTooLargeException.

Por que a tela composable() é recriada ao girar a tela?

A rotação da tela causa uma mudança de configuração, que por padrão recria a Activity. Para preservar o estado das telas composable, use rememberSaveable para dados simples ou ViewModel com o escopo dessa tela. O Navigation Compose restaura o back stack após a recriação, mas o estado dentro das funções composable() é redefinido sem rememberSaveable.

Posso usar composable() sem NavHost?

Não, composable() é uma função de extensão do NavGraphBuilder, que está disponível apenas dentro do bloco NavHost. Para substituição simples de UI sem navegação, use renderização condicional (when, if) ou AnimatedContent. composable() é projetado especificamente para roteamento com suporte a back stack e links profundos.

Como distinguir a primeira navegação de uma navegação de retorno no composable()?

Use SavedStateHandle dentro do ViewModel: na primeira navegação, handle.get("initialized") retorna null; na navegação de retorno, retorna o valor salvo. Alternativamente, analise a posição atual no back stack através de navController.previousBackStackEntry — se for null, esta é a primeira tela na pilha de navegação.

Resumo

  • composable() é uma função de registro de tela no NavHost, a principal forma de organizar a navegação no Jetpack Compose.
  • Rotas — cada tela é identificada por uma string de rota com parâmetros de caminho e consulta opcionais.
  • Argumentos — são passados via NavArgument com suporte para tipos primitivos, Parcelable e Serializable.
  • Aninhamento — composable() suporta NavHost aninhados para organizar navegação modular com pilhas independentes.
  • Desempenho — a inicialização preguiçosa de telas economiza memória, a velocidade de troca entre telas é de 5–15 ms.
  • Erros — principais problemas: recriação do NavController, recomposição infinita com navigate() no corpo do composable, manuseio incorreto do BottomNavigation.
  • Migração — migrar de FragmentManager para composable() reduz o volume de código de navegação em 40–60% e elimina uma classe de bugs relacionados ao ciclo de vida do Fragment.

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