NavController — Wesen, Methoden und Navigationsverwaltung in Jetpack Compose

Autor: IT Sectr Veröffentlicht: 2026-06-29 Lesezeit: 7 Min.

NavController ist die zentrale Komponente der Navigation Compose-Bibliothek, die den Navigationsstapel und den Back Stack-Zustand in Android-Anwendungen verwaltet. Über NavController werden Übergänge zwischen Bildschirmen, Rückkehr zu vorherigen Seiten und Datenübertragung zwischen Routen durchgeführt. Laut Android Developers (2025) ist NavController ein obligatorisches Element jeder Compose-Anwendung mit mehr als einem Bildschirm. Der Controller wird über rememberNavController() erstellt, an NavHost übergeben und steht zum Aufruf von navigate() von jedem Punkt der Komposition aus zur Verfügung. Die integrierte SavedStateHandle-Unterstützung speichert automatisch den ViewModel-Zustand bei Rekonfiguration.

Wichtige Punkte

  • NavController — der zentrale Compose-Navigationscontroller, der Back Stack und Übergänge zwischen Bildschirmen verwaltet
  • navigate() — die Hauptmethode zur Navigation zu einer Route mit NavOptions-Unterstützung für die Stapelverwaltung
  • popBackStack() — Rückkehr zum vorherigen Bildschirm mit optionaler Bereinigung bis zu einer angegebenen Route
  • SavedStateHandle — Integration mit ViewModel zur Erhaltung des Bildschirmzustands während der Navigation
  • currentBackStackEntryAsState() — Beobachtung der aktuellen Route zur UI-Synchronisation

Was ist NavController in Jetpack Compose?

NavController ist eine Klasse aus der Navigation Compose-Bibliothek, die den Navigationscontroller für Compose-Anwendungen implementiert. NavController verwaltet den NavBackStackEntry-Stapel, wobei jeder Eintrag die Route, Argumente und den Bildschirmzustand enthält. Der Controller unterstützt grundlegende Navigationsoperationen: Übergang, Rückkehr, Ersetzung und Bereinigung.

Anders als beim View-System, wo die Navigation über FragmentManager oder Intent erfolgte, arbeitet NavController ausschließlich im Compose-Kontext. Der Back Stack wird als NavDestination-Graph und nicht als Fragment-Stapel gespeichert. Dies eliminiert den Overhead beim Erstellen und Zerstören von Fragmenten und vereinfacht das Testen — NavController kann über TestNavHostController mockiert werden.

NavController ist eng mit NavHost verbunden — einem Container, der den aktuellen Bildschirm aus dem Graphen rendert. Ohne NavHost kann NavController keine composable-Funktionen anzeigen, behält aber die Fähigkeit, den Stapel zu verwalten. In einer typischen Architektur wird NavController auf Activity- oder Haupt-composable-Ebene erstellt und über Parameter nach unten im Kompositionsbaum übergeben.

Laut Google hat NavController mehrere Hauptversionen durchlaufen. Version 2.8.0 führte Type-Safe Navigation ein, Version 2.9.0 führte Unterstützung für predictive back gesture (Android 14+) ein. Der Controller ist kompatibel mit Material3 Scaffold und BottomNavigation. Für Multimodul-Projekte wird NavController über DI (Hilt/Koin) oder Konstruktorparameter übergeben.

NavController wird über die composable-Funktion rememberNavController() erstellt. Die Funktion gibt eine Instanz von NavHostController (eine Unterklasse von NavController) zurück, die an den Lebenszyklus des aktuellen composable gebunden ist. Beim Verlassen der Komposition wird der Controller gelöscht. Um den Controller während der Rekonfiguration zu erhalten, verwenden Sie rememberSaveable oder ViewModel.

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

Die NavController-Konfiguration umfasst: NavHostController (Haupt), TestNavHostController (Test) und ScopedNavController (Kind für verschachtelte Graphen). Für BottomNavigation sollte NavController für die gesamte App einheitlich sein — das Erstellen eines neuen Controllers in jedem Tab führt zu Stapelverlust. Um den Controller an verschachtelte Bildschirme zu übergeben, verwenden Sie einen Funktionsparameter anstelle von CompositionLocalProvider, um die Lesbarkeit zu erhalten.

Zum Testen der Navigation verwenden Sie TestNavHostController mit compose-test-rule. Der Controller ermöglicht das Festlegen der Startroute und die Überprüfung, ob navigate() den erwarteten Übergang ausgelöst hat. Das Testen von NavController erfordert keinen Emulator — es funktioniert mit den Semantics-Matchern von Compose Test.

Die Methode navigate(route: String) ist der primäre Navigationsmechanismus in NavController. Sie akzeptiert eine Routenzeichenfolge, optionale NavOptions und Navigator.Extras. NavOptions steuern das Übergangsverhalten: launchSingleTop (Route nicht im Stapel duplizieren), popUpTo (Stapel bis zu einer Route leeren), restoreState (vorherigen Zustand wiederherstellen).

NavOptions werden über die Builder-Syntax festgelegt: NavOptionsBuilder. Hauptparameter: popUpTo (Route + inclusive/saveState), launchSingleTop (Boolean, true — keine Duplikate erstellen), restoreState (Zustand bei Rückkehr wiederherstellen). Ohne popUpTo fügt jeder navigate()-Aufruf einen Eintrag zum Stapel hinzu, was zur Ansammlung des Back Stacks und falschem Verhalten der Back-Taste führt.

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

Navigator.Extras ermöglicht das Übergeben zusätzlicher Daten, die nicht Teil der Route sind: gemeinsame Elemente für Animationen, Intent-Flags, Pac-Man-Bundle. Extras werden selten verwendet — hauptsächlich für die Integration mit Accompanist Animation oder benutzerdefinierten Navigators. Für die meisten Szenarien reichen eine Routenzeichenfolge und NavOptions aus.

popBackStack: Verwaltung von Rückkehr und Stapelbereinigung

popBackStack() ist die Methode zur Rückkehr zum vorherigen Bildschirm. Ohne Argumente entfernt sie den obersten Stapeleintrag und gibt true zurück, wenn die Entfernung erfolgreich war. Wenn der Stapel leer ist, gibt die Methode false zurück und die Activity wird geschlossen (ähnlich super.onBackPressed()).

Die überladene Version popBackStack(route: String, inclusive: Boolean) entfernt alle Einträge bis zur angegebenen Route. Wenn inclusive = true ist, wird auch die angegebene Route selbst entfernt. Die Methode gibt Boolean zurück — true, wenn Einträge gefunden und entfernt wurden. Die Version mit inclusive ist nützlich für Szenarien wie „Austritt zum Startbildschirm“ nach Autorisierung oder Bestellabschluss.

MethodeBeschreibungBeispiel
popBackStack()Einen Bildschirm zurücknavController.popBackStack()
popBackStack(route, false)Bis route leeren (route bleibt)popBackStack(„home“, false)
popBackStack(route, true)Bis einschließlich route leerenpopBackStack(„home“, true)
navigate(route) { popUpTo(route) { inclusive = true } }Navigation mit vollständiger Bereinigungnavigate(„login“) { popUpTo(0) { inclusive = true } }

Zur Behandlung der systemischen Back-Taste (Hardware-Back-Button) verwenden Sie BackHandler aus Compose. BackHandler akzeptiert enabled und onBack — einen Callback, der beim Drücken aufgerufen wird. Für Android 14+ wird PredictiveBackGesture verwendet, das über NavController ab Version 2.9.0 integriert ist. Predictive back fügt eine Vorschau-Animation der Rückkehr hinzu.

SavedStateHandle: Erhaltung des Bildschirmzustands

SavedStateHandle ist ein Mechanismus zur Erhaltung des ViewModel-Zustands während Navigation und Rekonfiguration. NavController stellt automatisch SavedStateHandle für jeden NavBackStackEntry bereit. Über SavedStateHandle speichert ViewModel den Bildschirmzustand und stellt ihn bei Rückkehr wieder her (restoreState = true).

In Navigation Compose wird SavedStateHandle zusammen mit ViewModel verwendet: ViewModel wird über SavedStateHandle initialisiert, das von backStackEntry übergeben wird. Beim Navigieren zu einem anderen Bildschirm und Zurückkehren (mit restoreState) erhält ViewModel den gespeicherten Zustand, anstatt neu erstellt zu werden. Dies ist kritisch für Bildschirme mit Dateneingabe, Filtern oder Scrollen.

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

SavedStateHandle unterstützt primitive Typen, String, Bundle und Parcelable. Für komplexe Objekte speichern Sie nur IDs und laden die vollständigen Daten aus dem Repository. Das Limit von SavedStateHandle beträgt etwa 1 MB — Überschreitung verursacht TransactionTooLargeException. Für große Datenmengen verwenden Sie Room oder DataStore anstelle der Speicherung im Handle.

Wichtig: SavedStateHandle erhält den Zustand nur, wenn restoreState = true in NavOptions verwendet wird. Wenn restoreState nicht angegeben ist, wird ViewModel bei Rückkehr mit Standardwerten neu erstellt. Für BottomNavigation-Umschaltung mit restoreState erhält NavController den Zustand jedes Tabs und stellt ihn bei erneuter Auswahl wieder her.

Beobachtung der aktuellen Route über currentBackStackEntryAsState

currentBackStackEntryAsState() ist eine Funktion, die State<NavBackStackEntry?> zurückgibt, das sich bei jeder Änderung der aktuellen Route aktualisiert. Dies ist der primäre Mechanismus zur UI-Synchronisation mit der Navigation: BottomNavigation hebt das aktive Element hervor, Toolbar aktualisiert den Titel, Drawer schließt bei Übergang.

Die Funktion arbeitet über snapshotFlow und collectAsState: Wenn sich der Back Stack ändert, komponiert Compose abonnierte Elemente neu. Wichtig: currentBackStackEntryAsState() aktualisiert erst nach Abschluss der Übergangsanimation. Für sofortige Aktualisierungen verwenden Sie currentDestination, das sich synchron mit navigate() ändert, aber keinen Zustand unterstützt.

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

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

Um auf Argumente der aktuellen Route zuzugreifen, verwenden Sie navBackStackEntry?.arguments. Dies ist praktisch in BottomNavigation: selectedItem wird basierend auf currentRoute berechnet. Für Navigations-Debugging verwenden Sie NavController.addOnDestinationChangedListener(), das jeden Übergang protokolliert. In der Produktion vermeiden Sie Abonnements innerhalb vieler composables — erstellen Sie eine einzige Quelle in ViewModel und übergeben Sie State an die UI.

Häufig gestellte Fragen

Kann man mehrere NavController in einer Activity erstellen?

Technisch ja, aber nicht empfohlen. Ein einzelner NavController gewährleistet einen konsistenten Back Stack und vereinfacht das Debugging. Mehrere Controller sind nur für verschachtelte Graphen mit separater Navigation gerechtfertigt (z.B. modal bottom sheet mit eigenem Stapel).

Wie übergibt man NavController durch ViewModel?

Übergeben Sie NavController an ViewModel über Konstruktor oder DI. Besser ist es jedoch, nur Callback-Funktionen (onNavigate, onBack) statt NavController selbst zu übergeben — das vereinfacht das Testen. Für Ereignisse verwenden Sie Channel<NavEvent> in ViewModel und sammeln Sie in der UI.

Warum funktioniert navigate nach einer asynchronen Operation nicht?

Das Problem liegt im Lebenszyklus: Wenn NavController noch nicht initialisiert ist (NavHost nicht erstellt), wird navigate() ignoriert. Verwenden Sie LaunchedEffect, um die Navigation nach dem Laden von Daten aufzurufen, nicht innerhalb einer Coroutine mit beliebigem Lebenszyklus.

Wie leert man den gesamten Back Stack und navigiert zu einem neuen Bildschirm?

Rufen Sie navController.navigate(„target“) { popUpTo(0) { inclusive = true } } auf. Der Parameter popUpTo(0) leert den Stapel vollständig, inclusive = true entfernt auch den Starteintrag. Das Flag launchSingleTop = true verhindert Duplikate der neuen Route.

Was ist der Unterschied zwischen NavHostController und NavController?

NavHostController ist eine Unterklasse von NavController mit zusätzlichen Methoden für NavHost (z.B. setOnBackStackChangedListener). NavController ist die Basisklasse, die außerhalb von NavHost für programmatische Stapelverwaltung verwendet werden kann. In den meisten Fällen wird NavHostController verwendet.

Zusammenfassung

  • NavController — die zentrale Komponente von Navigation Compose, verwaltet den Routenstapel und Übergänge zwischen Bildschirmen
  • navigate() führt Übergänge mit popUpTo-, launchSingleTop- und restoreState-Einstellungen über NavOptions durch
  • popBackStack() verwaltet die Rückkehr: Einzelschritt oder Massenbereinigung bis zu einer angegebenen Route mit inclusive
  • SavedStateHandle integriert sich mit ViewModel zur automatischen Erhaltung des Bildschirmzustands während Navigation
  • currentBackStackEntryAsState() bietet reaktive Beobachtung der aktuellen Route zur UI-Synchronisation
  • BackHandler behandelt die systemische Back-Taste, PredictiveBackGesture wird ab NavController 2.9.0 unterstützt
  • Für Tests verwenden Sie TestNavHostController mit compose-test-rule und Semantics-Matchern

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch