NavController is de centrale component van de Navigation Compose-bibliotheek die de navigatiestapel en de status van de back stack in Android-applicaties beheert. Via NavController worden overgangen tussen schermen, terugkeer naar vorige pagina’s en gegevensoverdracht tussen routes uitgevoerd. Volgens Android Developers (2025) is NavController een verplicht onderdeel van elke Compose-applicatie met meer dan één scherm. De controller wordt aangemaakt via rememberNavController(), doorgegeven aan NavHost en is beschikbaar voor het aanroepen van navigate() vanaf elk punt in de compositie. Ingebouwde ondersteuning voor SavedStateHandle slaat automatisch de ViewModel-status op bij herconfiguratie.
Belangrijkste punten
NavController — is een klasse uit de Navigation Compose-bibliotheek die de navigatiecontroller voor Compose-applicaties implementeert. NavController beheert de NavBackStackEntry-stapel, waarbij elke invoer de route, argumenten en schermstatus bevat. De controller ondersteunt basisnavigatiebewerkingen: overgang, terugkeer, vervanging en opschoning.
In tegenstelling tot het View-systeem, waar navigatie via FragmentManager of Intent verliep, werkt NavController uitsluitend in de Compose-context. De back stack wordt opgeslagen als een NavDestination-graaf, niet als een Fragment-stapel. Dit elimineert de overhead van het maken en vernietigen van Fragment en vereenvoudigt het testen — NavController kan worden gemockt via TestNavHostController.
NavController is nauw verbonden met NavHost — de container die het huidige scherm uit de graaf rendert. Zonder NavHost kan NavController geen composable-functies weergeven, maar behoudt het de mogelijkheid om de stapel te beheren. In een typische architectuur wordt NavController gemaakt op het niveau van Activity of de hoofd-composable en doorgegeven via parameters in de compositieboom.
Volgens Google heeft NavController verschillende grote releases doorgemaakt. Versie 2.8.0 voegde Type-Safe Navigation toe, versie 2.9.0 — ondersteuning voor predictive back gesture (Android 14+). De controller is compatibel met Material3 Scaffold en BottomNavigation. Voor multimodulaire projecten wordt NavController doorgegeven via DI (Hilt/Koin) of constructorparameters.
NavController wordt aangemaakt via de composable-functie rememberNavController(). De functie retourneert een NavHostController-instantie (een descendant van NavController) die is gekoppeld aan de levenscyclus van de huidige composable. Bij het verlaten van de compositie wordt de controller opgeschoond. Gebruik rememberSaveable of ViewModel om de controller bij herconfiguratie te bewaren.
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "main"
) {
composable("main") { MainScreen(navController) }
composable("details") { DetailsScreen(navController) }
}
}
De configuratie van NavController omvat: NavHostController (hoofd), TestNavHostController (testen) en ScopedNavController (onderliggend voor geneste grafen). Voor BottomNavigation moet NavController uniek zijn voor de hele applicatie — het maken van een nieuwe controller in elk tabblad leidt tot verlies van de stapel. Gebruik voor het doorgeven van de controller aan geneste schermen de functieparameter in plaats van CompositionLocalProvider om de leesbaarheid te behouden.
Gebruik voor het testen van navigatie TestNavHostController met compose-test-rule. De controller maakt het mogelijk om de beginroute in te stellen en te verifiëren dat navigate() de verwachte overgang heeft aangeroepen. Testen van NavController vereist geen emulator — het werkt met Semantics-matchers van Compose Test.
De methode navigate(route: String) — de primaire manier van navigatie in NavController. Ontvangt een routestring, optionele NavOptions en Navigator.Extras. NavOptions beheren het overgangsgedrag: launchSingleTop (route niet dupliceren in de stapel), popUpTo (stapel opschonen tot route), restoreState (vorige status herstellen).
NavOptions worden ingesteld via builder-syntaxis: NavOptionsBuilder. Belangrijkste parameters: popUpTo (route + inclusive/saveState), launchSingleTop (Boolean, true — geen duplicaat maken), restoreState (status herstellen bij terugkeer). Zonder popUpTo voegt elke navigate() een invoer toe aan de stapel, wat leidt tot ophoping van de back stack en incorrect gedrag van de Back-knop.
navController.navigate("profile/42") {
popUpTo("main") { saveState = true }
launchSingleTop = true
restoreState = true
}
Navigator.Extras maakt het mogelijk om aanvullende gegevens door te geven die geen deel uitmaken van de route: shared element voor animatie, Intent-vlaggen, Pac-Man bundle. Extras worden zelden gebruikt — voornamelijk voor integratie met Accompanist Animation of aangepaste Navigator. Voor de meeste scenario’s zijn de routestring en NavOptions voldoende.
popBackStack() — methode voor terugkeer naar het vorige scherm. Zonder argumenten verwijdert het de bovenste stapelinvoer en retourneert true als het verwijderen is gelukt. Als de stapel leeg is — retourneert de methode false en wordt Activity gesloten (vergelijkbaar met super.onBackPressed()).
De overbelaste versie popBackStack(route: String, inclusive: Boolean) verwijdert alle invoeren tot de opgegeven route. Als inclusive = true is — wordt ook de opgegeven route zelf verwijderd. De methode retourneert Boolean — true als invoeren zijn gevonden en verwijderd. De versie met inclusive is nuttig voor scenario’s van „uitgang naar het hoofdscherm” na autorisatie of het plaatsen van een bestelling.
| Methode | Beschrijving | Voorbeeld |
|---|---|---|
| popBackStack() | Één scherm teruggaan | navController.popBackStack() |
| popBackStack(route, false) | Opschonen tot route (route blijft) | popBackStack("home", false) |
| popBackStack(route, true) | Opschonen tot en met route | popBackStack("home", true) |
| navigate(route) { popUpTo(route) { inclusive = true } } | Overgang met volledige opschoning | navigate("login") { popUpTo(0) { inclusive = true } } |
Gebruik BackHandler uit Compose voor het afhandelen van de systeem Back-knop (hardware back button). BackHandler accepteert enabled en onBack — callback die wordt aangeroepen bij indrukken. Voor Android 14+ wordt PredictiveBackGesture gebruikt, geïntegreerd via NavController vanaf versie 2.9.0. Predictive back voegt een voorbeeldanimatie van de terugkeer toe.
SavedStateHandle — is een mechanisme voor het opslaan van de ViewModel-status bij navigatie en herconfiguratie. NavController biedt automatisch SavedStateHandle voor elke NavBackStackEntry. Via SavedStateHandle slaat ViewModel de schermstatus op en herstelt deze bij terugkeer (restoreState = true).
In Navigation Compose wordt SavedStateHandle samen met ViewModel gebruikt: ViewModel wordt geïnitialiseerd via SavedStateHandle dat wordt doorgegeven vanuit backStackEntry. Bij overgang naar een ander scherm en terugkeer (met restoreState) ontvangt ViewModel de opgeslagen status en wordt niet opnieuw aangemaakt. Dit is cruciaal voor schermen met gegevensinvoer, filters of scrollen.
class ProfileViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val userId: String = savedStateHandle.get<String>("userId") ?: ""
var searchQuery by savedStateHandle.getStateFlow("search", "")
.collectAsState()
}
SavedStateHandle ondersteunt primitieve typen, String, Bundle en Parcelable. Sla voor complexe objecten alleen de ID op en laad de volledige gegevens uit de repository. De limiet van SavedStateHandle is ongeveer 1 MB, overschrijding veroorzaakt TransactionTooLargeException. Gebruik voor grote volumes Room of DataStore in plaats van opslaan in handle.
Belangrijk: SavedStateHandle slaat de status alleen op bij gebruik van restoreState = true in NavOptions. Als restoreState niet is opgegeven, wordt ViewModel bij terugkeer opnieuw aangemaakt met standaardwaarden. Voor het schakelen van BottomNavigation met restoreState slaat NavController de status van elk tabblad op en herstelt deze bij herhaalde selectie.
currentBackStackEntryAsState() — functie die State<NavBackStackEntry?> retourneert, die wordt bijgewerkt bij elke wijziging van de huidige route. Dit is het belangrijkste mechanisme voor UI-synchronisatie met navigatie: BottomNavigation markeert het actieve element, Toolbar werkt de titel bij, Drawer sluit bij overgang.
De functie werkt via snapshotFlow en collectAsState: bij wijziging van de back stack hercomponeert Compose de geabonneerde elementen. Belangrijk: currentBackStackEntryAsState() wordt pas bijgewerkt na voltooiing van de overgangsanimatie. Gebruik voor onmiddellijke bijwerking currentDestination, dat synchroon verandert met navigate(), maar geen status ondersteunt.
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
Text(
text = when (currentRoute) {
"home" -> "Home"
"profile" -> "Profile"
else -> ""
}
)
Gebruik navBackStackEntry?.arguments voor toegang tot de argumenten van de huidige route. Dit is handig in BottomNavigation: selectedItem wordt berekend op basis van currentRoute. Gebruik NavController.addOnDestinationChangedListener() voor navigatiedebugging, die elke overgang logt. Vermijd in productie abonnementen binnen een groot aantal composables — maak één bron aan in ViewModel en geef State door aan UI.
Veelgestelde vragen
Technisch gezien wel, maar het wordt niet aanbevolen. Een enkele NavController zorgt voor een consistente back stack en vereenvoudigt het debuggen. Meerdere controllers zijn alleen gerechtvaardigd voor geneste grafen met aparte navigatie (bijv. modal bottom sheet met eigen stapel).
Geef NavController door aan ViewModel via de constructor of DI. Het is echter beter om alleen callback-functies (onNavigate, onBack) door te geven, niet NavController zelf — dit vereenvoudigt het testen. Gebruik voor gebeurtenissen Channel<NavEvent> in ViewModel en verzamel in UI.
Het probleem zit in de levenscyclus: als NavController nog niet is geïnitialiseerd (NavHost is niet opgebouwd), wordt navigate() genegeerd. Gebruik LaunchedEffect om navigatie aan te roepen na het laden van gegevens, niet binnen een coroutine met een willekeurige lifecycle.
Roep navController.navigate("target") { popUpTo(0) { inclusive = true } } aan. Parameter popUpTo(0) maakt de stapel volledig leeg, inclusive = true verwijdert ook de startinvoer. De vlag launchSingleTop = true voorkomt duplicatie van de nieuwe route.
NavHostController — een descendant van NavController met extra methoden voor NavHost (bijv. setOnBackStackChangedListener). NavController — de basisklasse die buiten NavHost kan worden gebruikt voor programmatisch stapelbeheer. In de meeste gevallen wordt NavHostController gebruikt.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook