NavHost é um contêiner composable que serve como ponto de entrada para o grafo de navegação no Jetpack Compose. Ele vincula um NavController a um conjunto de rotas e renderiza a tela atual com base no estado da pilha de retorno. Segundo Android Developers (2025), NavHost é um componente obrigatório para qualquer aplicativo Compose com navegação. Dentro do NavHost, rotas composable são registradas com argumentos opcionais, deep links e animações. Cada rota é uma função composable comum que recebe um NavBackStackEntry com dados de transição. O NavHost lida automaticamente com o botão de voltar, salvamento de estado e restauração na reconfiguração.
Principais Pontos
NavHost é uma função composable que fornece um contêiner para exibir a tela de navegação atual. NavHost recebe um NavController, startDestination e um grafo de rotas construído via Kotlin DSL. Quando a rota atual muda, NavHost alterna o composable exibido com a animação especificada.
NavHost funciona como um alternador de telas: ele rastreia o NavBackStackEntry atual do NavController e renderiza o bloco composable correspondente. Cada tela é uma função composable independente que recebe um NavBackStackEntry com argumentos de rota. Todas as telas existem em uma única árvore de composição, mas NavHost mostra apenas uma por vez, ocultando as outras através de animação.
Ao contrário do FragmentManager, NavHost não cria um Fragment para cada tela. Todo o ciclo de vida é gerenciado através do CompositionLifecycle — funções composable não têm onStart/onResume, então LaunchedEffect e DisposableEffect são usados para efeitos colaterais. NavHost se inscreve automaticamente no NavController e recompõe a UI quando a rota muda.
Segundo o Google, NavHost é uma API estável desde Navigation 2.4.0. A partir da 2.8.0, NavHost suporta navegação Type-Safe através do Kotlin Serialization, substituindo rotas de string por classes de dados. NavHost também suporta grafos aninhados, permitindo organização modular da navegação.
NavHost é criado com dois parâmetros obrigatórios: navController (uma instância de NavHostController) e startDestination (a string de rota da primeira tela). O terceiro parâmetro é um bloco construtor onde todas as rotas são registradas via composable(), navigation() e dialog().
@Composable
fun AppNavHost(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") { HomeScreen(navController) }
composable("settings") { SettingsScreen(navController) }
}
}
startDestination é a rota que abre quando NavHost é iniciado pela primeira vez. Se a pilha de retorno estiver vazia, NavHost adiciona automaticamente startDestination à pilha. Na reconfiguração (rotação de tela), NavHost restaura a última rota de savedState, não startDestination.
Para BottomNavigation, startDestination é uma das rotas do painel inferior. As rotas restantes do painel são adicionadas como entradas composable separadas. NavHost deve ser colocado dentro de Scaffold.content — onde o conteúdo principal do aplicativo é exibido. NavHost ocupa toda a altura disponível menos TopAppBar e BottomNavigation.
A função composable(route, arguments, deepLinks, enterTransition, exitTransition, content) registra uma rota no grafo do NavHost. O parâmetro route é uma string que descreve o caminho com placeholders opcionais na forma {paramName}. O placeholder é substituído por um valor real durante a navegação.
O bloco content do composable recebe um NavBackStackEntry do qual os argumentos são extraídos. A função composable da tela é renderizada apenas quando a rota atual do NavController corresponde à rota. Em caso de incompatibilidade, o composable é removido da composição, mas seu estado pode ser preservado através de rememberSaveable ou ViewModel com SavedStateHandle.
composable(
route = "article/{articleId}",
arguments = listOf(navArgument("articleId") {
type = NavType.IntType
defaultValue = 0
}),
deepLinks = listOf(navDeepLink { uriPattern = "https://app.example/article/{articleId}" })
) { backStackEntry ->
val articleId = backStackEntry.arguments?.getInt("articleId") ?: 0
ArticleScreen(articleId = articleId)
}
O número de entradas composable dentro do NavHost pode variar de algumas a centenas. Para aplicações grandes, as rotas são divididas em módulos e conectadas via grafos aninhados. Cada composable pode ter suas próprias configurações de animação, deep links e argumentos.
Os argumentos de rota são definidos através do parâmetro arguments: List<NamedNavArgument> em composable(). Cada argumento é definido via navArgument(name) { type; defaultValue }. NavType determina o tipo de argumento: StringType, IntType, LongType, FloatType, BoolType, ParcelableType e ReferenceType.
| Parâmetro de rota | Exemplo de rota | NavType |
|---|---|---|
| Caminho (path) | "user/{id}" | NavType.IntType |
| Consulta (query) | "search?q={query}" | NavType.StringType |
| Opcional | "details/{id}?tab={tab}" | StringType + defaultValue="" |
| Parcelable | "checkout/{order}" | NavType.ParcelableType |
Os argumentos são extraídos de NavBackStackEntry via arguments?.getInt("id"). Para argumentos obrigatórios, defaultValue pode ser omitido — NavType usará null. Para argumentos opcionais, defaultValue deve ser definido, caso contrário a navegação lançará uma exceção se o parâmetro estiver ausente.
Desde Navigation 2.8.0, a navegação Type-Safe é recomendada: defina uma classe selada ou classe de dados para rotas com Kotlin Serialization. Em vez de uma rota de string, use composable<RouteType> { backStackEntry -> }. Isso elimina erros de digitação nas rotas e gera automaticamente NavType para argumentos. Para migrar, adicione a dependência navigation-compose-typesafe e o plugin Kotlin Serialization.
nested graphs — um mecanismo para agrupar rotas dentro do NavHost usando a função navigation(route, startDestination). Um grafo aninhado tem seu próprio prefixo de rota e startDestination, e todas as suas rotas são acessíveis através do prefixo. Grafos aninhados são usados para arquitetura modular, onde cada módulo de funcionalidade registra seu próprio subgrafo.
Benefícios dos grafos aninhados: isolamento de rotas dentro de um módulo, uma pilha de retorno unificada para um grupo de telas e a capacidade de navegar por prefixo sem expor a estrutura interna. Por exemplo, o grafo "auth" contém "auth/login" e "auth/register". A navegação é possível tanto pela rota completa quanto por prefixo com redirecionamento para startDestination.
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) }
}
}
Grafos aninhados suportam passagem de argumentos no nível do grafo: parâmetros declarados na rota do grafo são passados para todas as rotas internas. Para limpar um grafo aninhado, use popBackStack(route) — removerá todas as entradas internas. Grafos aninhados não têm limite de profundidade, mas não mais que 3 níveis são recomendados para legibilidade.
NavHost suporta animações de transição entre rotas composable através dos parâmetros enterTransition, exitTransition, popEnterTransition e popExitTransition. As animações são definidas uma vez para NavHost e se aplicam a todas as rotas, ou individualmente para cada composable. Por padrão, as animações estão desativadas.
Configuração típica: enterTransition = slideInHorizontally(initialOffsetX = { it }) — a tela desliza da direita; exitTransition = slideOutHorizontally(targetOffsetX = { -it }) — a tela desliza para a esquerda. Para animação pop, as direções são espelhadas: a tela desliza da esquerda e desliza para a direita. Para BottomNavigation, usa-se fadeIn/fadeOut sem deslizamento.
NavHost(
navController = navController,
startDestination = "home",
enterTransition = { slideInHorizontally(initialOffsetX = { it }) + fadeIn() },
exitTransition = { slideOutHorizontally(targetOffsetX = { -it }) + fadeOut() },
popEnterTransition = { slideInHorizontally(initialOffsetX = { -it }) + fadeIn() },
popExitTransition = { slideOutHorizontally(targetOffsetX = { it }) + fadeOut() }
) { /* composable routes */ }
Animações personalizadas são criadas usando Compose Animation API: AnimatedContentTransitionScope fornece acesso às dimensões do contêiner, progresso da animação e direção. Para transições de elemento compartilhado (um elemento se movendo suavemente para outra tela), a biblioteca Accompanist Navigation Animation ou uma implementação personalizada via sharedElement Modifier é necessária. Segundo Android Developers (2025), a animação de deslizamento padrão (entrada pela direita, saída pela esquerda) é usada em 80% dos aplicativos Android com navegação.
Perguntas Frequentes
Tecnicamente sim, mas não é recomendado. Cada NavHost cria uma pilha de retorno independente, quebrando a navegação unificada. A exceção são áreas separadas, como um NavHost para o conteúdo principal e um NavHost para um BottomSheet com navegação própria.
NavHost é um contêiner de navegação que alterna telas. Scaffold é o layout de toda a página (TopAppBar, BottomNavigation, FloatingActionButton). Normalmente, NavHost é colocado dentro de Scaffold.content. Scaffold não gerencia navegação, apenas fornece slots para componentes de UI.
Um ViewModel é criado dentro de um NavBackStackEntry através de viewModel(). Para compartilhar um ViewModel entre telas, use parentNavController: vincule o ViewModel compartilhado à entrada pai. Uma alternativa é DI (Hilt/Koin) com escopo NavGraph.
Este é um comportamento normal — NavHost remove o composable da composição ao sair de uma rota. Para preservar o estado, use rememberSaveable para estado de UI e ViewModel com SavedStateHandle para lógica de negócios.
Adicione uma rota final composable("404") e navegue para ela quando um deep link desconhecido for recebido. NavHost não tem uma rota catch-all — verifique a rota no manipulador de intent Deep Link antes de navigate(). Se a rota não for encontrada, navegue para 404.
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