Navigation Compose é uma biblioteca Jetpack para navegação declarativa dentro de aplicações Android construídas com Jetpack Compose. Em vez de FragmentManager ou navegação baseada em Intent, o Navigation Compose oferece um grafo de rotas único gerenciado através de NavController e NavHost. De acordo com o Google I/O (2025), o Navigation Compose é o método de navegação recomendado para aplicações Compose, usado em mais de 70% dos novos projetos. A biblioteca suporta passagem de argumentos tipados, links profundos, animações de transição e integração com ViewModel através do SavedStateHandle.
Principais pontos
Navigation Compose é uma biblioteca do conjunto Jetpack que fornece um framework de navegação para aplicações Compose. A biblioteca é baseada nos mesmos princípios do Componente de Navegação para o sistema View, mas adaptada à natureza declarativa do Compose: em vez de FragmentTransaction, são usadas funções composable, e o grafo de navegação é construído via Kotlin DSL.
A principal diferença entre o Navigation Compose e a navegação clássica é a ausência do FragmentManager. Cada tela é uma função composable que é renderizada no NavHost quando a rota corresponde. A pilha de retorno armazena não um Fragment, mas um registro com rota, argumentos e estado. Isso simplifica a arquitetura e elimina os conflitos de ciclo de vida típicos da navegação baseada em Fragment.
De acordo com o Google (2025), o Navigation Compose passou de experimental para estável e faz parte do Jetpack desde a versão 2.8.0. A biblioteca suporta Material3, navegação type-safe (via Kotlin Serialization), grafos aninhados e modularização. A única limitação é que a biblioteca não suporta pilha de retorno múltipla para BottomNavigation sem configuração manual, embora o Google esteja trabalhando nisso.
A arquitetura do Navigation Compose gira em torno de três entidades: NavController (gerenciamento da pilha), NavHost (contêiner do grafo) e NavDestination (rota individual com composable). A interação entre elas é declarativa: o desenvolvedor descreve as rotas e os argumentos, e a biblioteca lida com os estados de carregamento, preservação e restauração.
NavController é o elemento central do Navigation Compose, que gerencia a pilha de navegação. É criado via rememberNavController() e passado para o NavHost. O NavController armazena a pilha de retorno, o ponto de entrada atual e suporta ações adiadas (deeplink após a inicialização do grafo).
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("profile/{userId}") { backStackEntry ->
ProfileScreen(
userId = backStackEntry.arguments?.getString("userId") ?: ""
)
}
}
}
Principais métodos do NavController: navigate(route) — navegar para uma rota, popBackStack() — retornar à tela anterior, navigateAndClear(route) — navegar e limpar a pilha. NavOptions especificam o comportamento: launchSingleTop evita duplicatas, popUpTo limpa a pilha até a rota especificada, restoreState restaura o estado anterior.
Para acessar o NavController de funções composable profundamente aninhadas, use NavHostController através do CompositionLocal. O LocalNavController é fornecido no ScopedNavController dentro do NavHost. Fora do NavHost (por exemplo, no BottomNavigation), o controlador é passado através de parâmetros ou ViewModel.
NavHost é um contêiner composable que vincula o NavController ao grafo de rotas. Cada rota é declarada através de composable(route, arguments, deepLinks), onde route é uma string de rota com placeholders {param} opcionais. Quando a rota atual corresponde, o NavHost renderiza o bloco composable correspondente.
O grafo de rotas é construído hierarquicamente: os grafos podem ser aninhados através de navigation() para agrupar rotas dentro de um módulo. Grafos aninhados têm seu próprio startDestination e são combinados sob um prefixo de rota comum. Isso permite organizar uma arquitetura modular onde cada módulo de funcionalidade registra seu próprio subgrafo.
NavHost(
navController = navController,
startDestination = "main"
) {
composable("main") { MainScreen(navController) }
navigation(
route = "auth",
startDestination = "auth/login"
) {
composable("auth/login") { LoginScreen(navController) }
composable("auth/register") { RegisterScreen(navController) }
}
}
O NavHost lida automaticamente com o botão Voltar do sistema (back press) através do LocalBackDispatcher. No Material3 Scaffold, ele por padrão captura o NavController para a operação correta do BottomNavigation. O NavHost recria o composable quando a rota muda, mas preserva o estado através de rememberSaveable para campos de entrada e rolagem.
O Navigation Compose suporta a passagem de argumentos tipados entre telas através de parâmetros de rota e NavType. Os parâmetros são especificados na rota como {paramName} com o tipo indicado através de arguments em composable(). O NavType suporta String, Int, Long, Float, Boolean, Parcelable e Serializable.
| Tipo de argumento | NavType | Exemplo de rota |
|---|---|---|
| String | NavType.StringType | "profile/{name}" |
| Int | NavType.IntType | "item/{id}" |
| Boolean | NavType.BoolType | "settings?enabled={flag}" |
| Parcelable | NavType.ParcelableType | "details/{item}" |
| Float | NavType.FloatType | "map?lat={lat}&lng={lng}" |
Os argumentos são extraídos de NavBackStackEntry através de arguments?.getType(key). Para parâmetros obrigatórios use defaultValue, para opcionais — nullable. O suporte a Parcelable funciona apenas com Kotlin Parcelize ou a biblioteca kotlinx.parcelize. Para objetos complexos, é recomendado passar um ID e carregar dados através do ViewModel, em vez de serializar o objeto inteiro.
Desde o Navigation 2.8.0, foi introduzida a navegação type-safe com Kotlin Serialization: as rotas são definidas como classes de dados e os argumentos como campos. Isso substitui as rotas de string por objetos tipados e elimina erros nos nomes das rotas. A migração requer o plugin Kotlin Serialization e a dependência navigation-compose-typesafe.
@Serializable
/* sealed class Route */
sealed class ProfileRoute(val route: String) {
data object Home : ProfileRoute("home")
data class Profile(val userId: String) : ProfileRoute("profile/{userId}")
}
Links profundos é um mecanismo de navegação que permite abrir uma tela específica do aplicativo através de uma URL ou intent-filter. No Navigation Compose, os links profundos são configurados através do parâmetro deepLinks em composable() e são processados automaticamente quando a URI corresponde ao padrão.
Um link profundo é especificado como uma lista de UriPattern: "https://example.com/profile/{userId}". Os parâmetros da URI são mapeados automaticamente para os argumentos da rota. O NavController processa links profundos na inicialização do aplicativo (via intent) e durante a operação (via links profundos implícitos). Para lidar com links profundos pendentes, use handleDeepLink() no NavController após a inicialização do grafo.
De acordo com o Google, links profundos são recomendados para: notificações push (Firebase Dynamic Links), verificação de e-mail, compartilhamento de conteúdo e navegação cruzada a partir de links da web. Para Android 12+, são usados Digital Asset Links para verificar a autoridade do link profundo. O AndroidManifest.xml deve conter um intent-filter com autoVerify="true" para abrir links sem diálogo.
composable(
route = "profile/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.StringType }),
deepLinks = listOf(
navDeepLink { uriPattern = "https://example.com/profile/{userId}" }
)
) { backStackEntry ->
ProfileScreen(userId = backStackEntry.arguments?.getString("userId") ?: "")
}
Limitações dos links profundos no Navigation Compose: a biblioteca não suporta links profundos adiados — um link profundo é processado apenas depois que o NavHost construiu totalmente o grafo. Se um link profundo chegar antes da inicialização do grafo, ele deve ser adiado via intent?.data e processado no LaunchedEffect. Para Firebase Dynamic Links, use o Firebase Dynamic Links SDK junto com o Navigation Compose.
O Navigation Compose suporta animações de transição através dos parâmetros enterTransition, exitTransition, popEnterTransition e popExitTransition em composable(). As animações são implementadas usando a Compose Animation API: fadeIn, slideInHorizontally, expandIn e outras. Por padrão, a animação está desativada — as telas são substituídas instantaneamente.
Cenários típicos de animação: slideInHorizontally para navegação para frente (a tela entra pela direita), slideOutHorizontally para retorno (a tela sai pela direita). Para BottomNavigation, a animação de desvanecimento sem deslizamento é mais comumente usada. As animações são definidas através do NavHost e se aplicam a todos os composables, a menos que animações individuais sejam especificadas.
NavHost(
navController = navController,
startDestination = "home",
enterTransition = { slideInHorizontally() + fadeIn() },
exitTransition = { slideOutHorizontally() + fadeOut() },
popEnterTransition = { fadeIn() },
popExitTransition = { slideOutHorizontally() + fadeOut() }
) { /* composable */ }
As animações podem ser substituídas para cada composable individualmente passando parâmetros de animação diretamente em composable(). É importante que as animações não entrem em conflito com a animação de retorno do sistema. Para transição de elemento compartilhado, é necessária a biblioteca accompanist-navigation-animation ou uma implementação personalizada através de Modifier.graphicsLayer. De acordo com o Android Developers (2025), 80% dos aplicativos de produção usam animação de deslizamento horizontal para navegação padrão.
Perguntas frequentes
Navigation Compose funciona sem Fragment, usando funções composable e Kotlin DSL para o grafo. O Componente de Navegação (View) é baseado em FragmentManager e grafos XML. A versão Compose é mais simples, mais rápida e não tem ciclos de vida de Fragment. O Componente de Navegação para View é adequado apenas para aplicações híbridas.
É recomendado passar o ID do objeto e carregar os dados através do ViewModel com SavedStateHandle. Se o objeto for simples, use Parcelable via kotlinx.parcelize. Passar objetos grandes diretamente através de argumentos (Bundle) é limitado a ~1 MB e pode causar TransactionTooLargeException.
Sim, através da função navigation(route, startDestination) dentro do NavHost. Grafos aninhados têm seu próprio startDestination e são combinados sob um prefixo de rota comum. Isso permite organizar uma arquitetura modular com grafos isolados para cada módulo de funcionalidade.
O NavController lida automaticamente com o pressionamento do botão Voltar através do BackHandler do Compose. Chame navController.popBackStack() quando Voltar for pressionado. Para tratamento personalizado (confirmação de saída), use BackHandler(enabled = condition) { callback } antes de chamar popBackStack().
Atualmente, o Navigation Compose não é suportado no Compose Multiplatform. Para a parte iOS de projetos multiplataforma, use Voyager ou Decompose. O Google está trabalhando no suporte a KMP, mas não há previsão de lançamento. Para projetos apenas Android, o Navigation Compose é a única opção recomendada.
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