NavController — essência, métodos e gerenciamento de navegação no Jetpack Compose

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

NavController é o componente central da biblioteca Navigation Compose que gerencia a pilha de navegação e o estado do back stack em aplicativos Android. Através do NavController são realizadas transições entre telas, retorno a páginas anteriores e transferência de dados entre rotas. De acordo com Android Developers (2025), NavController é um elemento obrigatório de qualquer aplicativo Compose com mais de uma tela. O controlador é criado via rememberNavController(), passado para NavHost e está disponível para chamar navigate() de qualquer ponto da composição. O suporte integrado do SavedStateHandle salva automaticamente o estado do ViewModel durante a reconfiguração.

Pontos Principais

  • NavController — o controlador central de navegação do Compose, gerenciando back stack e transições entre telas
  • navigate() — o método principal para navegar a uma rota com suporte a NavOptions para gerenciamento de pilha
  • popBackStack() — retorno à tela anterior com limpeza opcional até uma rota especificada
  • SavedStateHandle — integração com ViewModel para preservar o estado da tela durante a navegação
  • currentBackStackEntryAsState() — observação da rota atual para sincronização da UI

O que é NavController no Jetpack Compose?

NavController é uma classe da biblioteca Navigation Compose que implementa o controlador de navegação para aplicativos Compose. NavController gerencia a pilha NavBackStackEntry, onde cada entrada contém a rota, argumentos e estado da tela. O controlador suporta operações básicas de navegação: transição, retorno, substituição e limpeza.

Ao contrário do sistema View, onde a navegação era feita através de FragmentManager ou Intent, NavController funciona exclusivamente no contexto Compose. O back stack é armazenado como um grafo NavDestination em vez de uma pilha de Fragment. Isso elimina a sobrecarga de criar e destruir Fragments e simplifica os testes — NavController pode ser simulado através de TestNavHostController.

NavController está intimamente ligado ao NavHost — um contêiner que renderiza a tela atual do grafo. Sem NavHost, NavController não pode exibir funções composable, mas mantém a capacidade de gerenciar a pilha. Em uma arquitetura típica, NavController é criado no nível da Activity ou composable principal e passado para baixo na árvore de composição através de parâmetros.

De acordo com o Google, NavController passou por vários grandes lançamentos. A versão 2.8.0 adicionou Type-Safe Navigation, a versão 2.9.0 adicionou suporte para predictive back gesture (Android 14+). O controlador é compatível com Material3 Scaffold e BottomNavigation. Para projetos multimódulo, NavController é passado através de DI (Hilt/Koin) ou parâmetros de construtor.

NavController é criado através da função composable rememberNavController(). A função retorna uma instância de NavHostController (subclasse de NavController) vinculada ao ciclo de vida do composable atual. Ao sair da composição, o controlador é limpo. Para preservar o controlador durante a reconfiguração, use rememberSaveable ou ViewModel.

kotlin
@Composable
fun MyApp() {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        startDestination = "main"
    ) {
        composable("main") { MainScreen(navController) }
        composable("details") { DetailsScreen(navController) }
    }
}

A configuração do NavController inclui: NavHostController (principal), TestNavHostController (testes) e ScopedNavController (filho para grafos aninhados). Para BottomNavigation, NavController deve ser único para todo o aplicativo — criar um novo controlador em cada guia resultará em perda da pilha. Para passar o controlador para telas aninhadas, use um parâmetro de função em vez de CompositionLocalProvider para manter a legibilidade.

Para testar a navegação, use TestNavHostController com compose-test-rule. O controlador permite definir a rota inicial e verificar se navigate() acionou a transição esperada. Testar NavController não requer emulador — funciona com os matchers Semantics do Compose Test.

O método navigate(route: String) é o mecanismo principal de navegação no NavController. Aceita uma string de rota, NavOptions opcionais e Navigator.Extras. NavOptions controlam o comportamento da transição: launchSingleTop (não duplicar a rota na pilha), popUpTo (limpar a pilha até uma rota), restoreState (restaurar estado anterior).

NavOptions são definidos através da sintaxe builder: NavOptionsBuilder. Parâmetros principais: popUpTo (rota + inclusive/saveState), launchSingleTop (Boolean, true — não criar duplicatas), restoreState (restaurar estado ao retornar). Sem popUpTo, cada navigate() adiciona uma entrada à pilha, levando ao acúmulo de back stack e comportamento incorreto do botão Back.

kotlin
navController.navigate("profile/42") {
    popUpTo("main") { saveState = true }
    launchSingleTop = true
    restoreState = true
}

Navigator.Extras permite passar dados adicionais que não fazem parte da rota: elementos compartilhados para animação, flags Intent, bundle Pac-Man. Extras são raramente usados — principalmente para integração com Accompanist Animation ou Navigators personalizados. Para a maioria dos cenários, uma string de rota e NavOptions são suficientes.

popBackStack: gerenciamento de retorno e limpeza de pilha

popBackStack() é o método para retornar à tela anterior. Sem argumentos, remove a entrada do topo da pilha e retorna true se a remoção foi bem-sucedida. Se a pilha estiver vazia, o método retorna false e a Activity fecha (similar a super.onBackPressed()).

A versão sobrecarregada popBackStack(route: String, inclusive: Boolean) remove todas as entradas até a rota especificada. Se inclusive = true, a própria rota especificada também é removida. O método retorna Boolean — true se as entradas foram encontradas e removidas. A versão com inclusive é útil para cenários de “saída para a tela raiz” após autorização ou finalização de pedido.

MétodoDescriçãoExemplo
popBackStack()Retornar uma tela atrásnavController.popBackStack()
popBackStack(route, false)Limpar até route (route permanece)popBackStack(“home”, false)
popBackStack(route, true)Limpar até e incluindo routepopBackStack(“home”, true)
navigate(route) { popUpTo(route) { inclusive = true } }Navegar com limpeza completanavigate(“login”) { popUpTo(0) { inclusive = true } }

Para lidar com o botão Back do sistema (hardware back button), use BackHandler do Compose. BackHandler aceita enabled e onBack — um callback invocado ao pressionar. Para Android 14+, é usado PredictiveBackGesture, integrado via NavController desde a versão 2.9.0. Predictive back adiciona uma animação de pré-visualização do retorno.

SavedStateHandle: preservação do estado da tela

SavedStateHandle é um mecanismo para preservar o estado do ViewModel durante navegação e reconfiguração. NavController fornece automaticamente SavedStateHandle para cada NavBackStackEntry. Através do SavedStateHandle, ViewModel armazena o estado da tela e o restaura ao retornar (restoreState = true).

No Navigation Compose, SavedStateHandle é usado junto com ViewModel: ViewModel é inicializado via SavedStateHandle, que é passado de backStackEntry. Ao navegar para outra tela e retornar (com restoreState), ViewModel recebe o estado salvo em vez de ser criado novamente. Isso é crítico para telas com entrada de dados, filtros ou rolagem.

kotlin
class ProfileViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    val userId: String = savedStateHandle.get<String>("userId") ?: ""
    var searchQuery by savedStateHandle.getStateFlow("search", "")
        .collectAsState()
}

SavedStateHandle suporta tipos primitivos, String, Bundle e Parcelable. Para objetos complexos, salve apenas IDs e carregue os dados completos do repositório. O limite do SavedStateHandle é de aproximadamente 1 MB, excedê-lo causa TransactionTooLargeException. Para grandes volumes, use Room ou DataStore em vez de salvar no handle.

Importante: SavedStateHandle preserva o estado apenas quando restoreState = true é usado em NavOptions. Se restoreState não for especificado, ViewModel é criado novamente com valores padrão ao retornar. Para alternância de BottomNavigation com restoreState, NavController preserva o estado de cada guia e o restaura ao ser reselecionada.

Observação da rota atual via currentBackStackEntryAsState

currentBackStackEntryAsState() é uma função que retorna State<NavBackStackEntry?>, que atualiza a cada mudança da rota atual. Este é o mecanismo principal para sincronização da UI com a navegação: BottomNavigation destaca o item ativo, Toolbar atualiza o título, Drawer fecha ao navegar.

A função funciona através de snapshotFlow e collectAsState: quando o back stack muda, Compose recompoe os elementos inscritos. Importante: currentBackStackEntryAsState() atualiza apenas após a conclusão da animação de transição. Para atualizações imediatas, use currentDestination, que muda sincronicamente com navigate() mas não suporta estado.

kotlin
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route

Text(
    text = when (currentRoute) {
        "home" -> "Home"
        "profile" -> "Profile"
        else -> ""
    }
)

Para acessar argumentos da rota atual, use navBackStackEntry?.arguments. Isso é conveniente no BottomNavigation: selectedItem é calculado com base em currentRoute. Para depuração de navegação, use NavController.addOnDestinationChangedListener() que registra cada transição. Em produção, evite inscrições dentro de um grande número de composables — crie uma única fonte no ViewModel e passe State para a UI.

Perguntas Frequentes

Posso criar vários NavControllers em uma Activity?

Tecnicamente sim, mas não é recomendado. Um único NavController garante um back stack consistente e simplifica a depuração. Múltiplos controladores são justificados apenas para grafos aninhados com navegação separada (por exemplo, modal bottom sheet com sua própria pilha).

Como passar NavController através do ViewModel?

Passe NavController para ViewModel via construtor ou DI. No entanto, é melhor passar apenas funções callback (onNavigate, onBack) em vez do NavController em si — isso simplifica os testes. Para eventos, use Channel<NavEvent> no ViewModel e colete na UI.

Por que navigate não funciona após uma operação assíncrona?

O problema é o ciclo de vida: se NavController ainda não foi inicializado (NavHost não construído), navigate() é ignorado. Use LaunchedEffect para chamar a navegação após carregar os dados, não dentro de uma corrotina com ciclo de vida arbitrário.

Como limpar todo o back stack e navegar para uma nova tela?

Chame navController.navigate(“target”) { popUpTo(0) { inclusive = true } }. O parâmetro popUpTo(0) limpa a pilha completamente, inclusive = true também remove a entrada inicial. A flag launchSingleTop = true evita rotas duplicadas.

Qual a diferença entre NavHostController e NavController?

NavHostController é uma subclasse de NavController com métodos adicionais para NavHost (por exemplo, setOnBackStackChangedListener). NavController é a classe base que pode ser usada fora do NavHost para gerenciamento programático da pilha. Na maioria dos casos, NavHostController é usado.

Resumo

  • NavController — o componente central do Navigation Compose, gerenciando a pilha de rotas e transições entre telas
  • navigate() realiza transições com configurações popUpTo, launchSingleTop e restoreState via NavOptions
  • popBackStack() gerencia o retorno: passo único ou limpeza em massa até uma rota especificada com inclusive
  • SavedStateHandle integra-se com ViewModel para preservação automática do estado da tela durante navegação
  • currentBackStackEntryAsState() fornece observação reativa da rota atual para sincronização da UI
  • BackHandler lida com o botão Back do sistema, e PredictiveBackGesture é suportado desde NavController 2.9.0
  • Para testes, use TestNavHostController com compose-test-rule e matchers Semantics

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