composable(): was es ist, NavHost und Routing in Jetpack Compose

Autor: IT Sectr Veröffentlicht: 2026-06-30 Lesezeit: 9 Min.

composable() ist eine Funktion der Navigation Compose-Bibliothek, die einen Bildschirm im NavHost registriert und eine URL-Route mit dem Compose-Layout verbindet. Wenn die Navigation zu einer bestimmten Route wechselt, ruft Jetpack Compose die entsprechende composable-Funktion auf und zeigt sie als aktuellen Bildschirm an. Im Gegensatz zu FragmentManager oder Intent-basierter Navigation arbeitet composable() auf der Ebene einer einzigen Activity und wird vollständig über Kotlin DSL gesteuert. Laut Android Developers (2025) verwenden mehr als 73% der modernen Android-Anwendungen, die mit Jetpack Compose erstellt wurden, Navigation Compose für Bildschirmübergänge.

Wichtige Punkte

  • composable() ist eine Funktion zum Registrieren eines Bildschirms im NavHost der Navigation Compose-Bibliothek.
  • Route — jeder Bildschirm wird durch eine als erstes Argument übergebene Zeichenfolgenroute identifiziert.
  • Parameter — composable() unterstützt Argumente über NavArgument, einschließlich erforderlicher und optionaler.
  • Verschachtelung — verschachtelte Navigation wird durch verschachtelte NavHosts mit separaten Routengraphen unterstützt.
  • Leistung — composable() verwendet lazy-Initialisierung: der Bildschirm wird nur bei der ersten Navigation erstellt.

Was ist composable() im NavHost

composable() ist eine Erweiterungsfunktion des NavHost-Objekts. Kotlin DSL ermöglicht es, sie innerhalb des NavHost-Blocks aufzurufen, um alle Bildschirme der Anwendung deklarativ zu beschreiben. Jeder Aufruf erstellt einen Eintrag im Navigationsgraphen, der eine Zeichenfolgenroute mit einer composable-Funktion verknüpft. Wenn ein Benutzer zu einer bestimmten Route navigiert, zeigt NavHost das entsprechende composable als aktuellen Bildschirm an und blendet den vorherigen aus.

Die Navigation Compose-Bibliothek wurde von Google im Jahr 2021 als Alternative zur Fragment-basierten Navigation für Jetpack Compose eingeführt. Der Hauptvorteil ist die vollständige Kompatibilität mit dem Compose-Paradigma: composable() arbeitet im selben Lebenszyklus wie andere Compose-Komponenten, ohne dass FragmentManager oder Transaktionen erforderlich sind. Dies eliminiert eine Klasse von Fehlern, die mit der Nichtübereinstimmung der Lebenszyklen von Fragment und Compose zusammenhängen.

Jedes composable() nimmt eine Zeichenfolgenroute und eine Lambda-Funktion entgegen, die ein NavBackStackEntry-Objekt erhält und Composable UI zurückgibt. Innerhalb des Lambdas kann über navController aus dem Gültigkeitsbereich auf NavController zugegriffen werden, was die Navigation zu anderen Bildschirmen ermöglicht. Diese Architektur macht die Navigation explizit und vorhersagbar.

kotlin
@Composable
fun AppNavigation() {
    val navController = rememberNavController()
    
    NavHost(
        navController = navController,
        startDestination = "home"
    ) {
        composable("home") {
            HomeScreen(
                onNavigateToProfile = {
                    navController.navigate("profile")
                }
            )
        }
        composable("profile") {
            ProfileScreen(
                onBack = { navController.popBackStack() }
            )
        }
    }
}

Wie composable() funktioniert: Schlüssel und Parameter

Jeder Aufruf von composable() erstellt einen Knoten mit einer eindeutigen Routenkennung im internen Graphen des NavHost. Wenn NavController navigate() ausführt, vergleicht die Bibliothek die angeforderte Route mit allen registrierten composable-Knoten und findet eine Übereinstimmung. Nach der Übereinstimmung wird ein NavBackStackEntry erstellt, auf den Navigationsstapel gelegt und die UI-Komposition gestartet.

Die interne Implementierung von composable() verwendet einen Lazy-Initialisierungs-Mechanismus: Die Bildschirmkomposition erfolgt nur bei der ersten Navigation zu dieser Route. Das bedeutet, dass Bildschirme, zu denen der Benutzer noch nie navigiert ist, keinen Speicher belegen und keinen Code ausführen. Dieser Ansatz verbessert die Leistung in Anwendungen mit vielen Bildschirmen erheblich.

Der key-Parameter in composable() ermöglicht die Verwaltung der Bildschirmneuerstellung. Standardmäßig wird composable bei wiederholter Navigation zur selben Route nicht neu erstellt — NavHost verwendet den vorhandenen Back-Stack-Eintrag. Wenn jedoch ein key übergeben wird und sich dieser ändert, erstellt NavHost eine neue Instanz der composable-Funktion. Dies ist nützlich für Bildschirme mit dynamischen Daten, bei denen beim erneuten Öffnen ein Zustands-Update erzwungen werden muss.

kotlin
val NavGraphBuilder.Composable: Unit
    get() = composable(
        route = "details/{itemId}",
        arguments = listOf(
            NavArgument("itemId") { 
                type = NavType.IntType
            }
        ),
        deepLinks = listOf(
            navDeepLink { uriPattern = "myapp://details/{itemId}" }
        )
    ) { backStackEntry ->
        val itemId = backStackEntry.arguments?.getInt("itemId") ?: 0
        DetailsScreen(itemId = itemId)
    }

Argumente über composable() übergeben

composable() unterstützt ein flexibles Argumentsystem über den Parameter arguments. Jedes Argument wird durch ein NavArgument-Objekt beschrieben, das den Typ, den Standardwert und die Erforderlichkeit definiert. Argumente werden in der Route als Pfadparameter (über geschweifte Klammern) oder als Abfrageparameter (über ein Fragezeichen) übergeben.

Pfadparameter werden direkt in der Routenvorlage angegeben: "profile/{userId}". Bei der Navigation zu "profile/42" extrahiert NavHost automatisch den Wert 42 und macht ihn über backStackEntry.arguments zugänglich. Abfrageparameter werden nach dem Fragezeichen hinzugefügt: "search?query={text}" und werden ebenfalls automatisch von der Bibliothek geparst.

Beim Extrahieren von Argumenten ist es wichtig, die Erforderlichkeit des Parameters über NavType.isNullableAllowed zu prüfen und Standardwerte über NavArgument defaultValue bereitzustellen. Wenn ein erforderlicher Parameter fehlt, löst Navigation Compose eine IllegalArgumentException aus, die subtile Fehler mit falschen Routen verhindert.

ArgumenttypNavTypeBeispiel in Route
IntNavType.IntType"item/{id}"
StringNavType.StringType"user/{name}"
BooleanNavType.BoolType"filter?enabled={value}"
FloatNavType.FloatType"map/{lat}/{lon}"
LongNavType.LongType"article/{timestamp}"

Für die Übergabe komplexer Objekte wird die Verwendung von NavType.ParcelableType oder NavType.SerializableType empfohlen. Google rät jedoch, die Größe der übertragenen Daten zu minimieren — es ist besser, eine Kennung zu übergeben und das Objekt innerhalb des Bildschirms anhand der ID zu laden. Dies verhindert Probleme mit großen serialisierten Daten und vereinfacht die Behandlung von Konfigurationsänderungen.

kotlin
data class Profile(val id: Int, val name: String) : Parcelable

            // Mit minimalen Daten navigieren
navController.navigate("profile/42")

            // Argumente auf dem Bildschirm abrufen
composable(
    route = "profile/{userId}",
    arguments = listOf(
        NavArgument("userId") { type = NavType.IntType }
    )
) { backStackEntry ->
    val userId = backStackEntry.arguments?.getInt("userId") ?: 0
    ProfileDetailScreen(userId = userId)
}

Verschachtelte Navigation mit composable()

In realen Anwendungen ist es oft notwendig, verschachtelte Navigationsgraphen zu organisieren — zum Beispiel einen separaten Bildschirmstapel innerhalb eines BottomNavigation-Tabs. composable() unterstützt die Verschachtelung durch verschachtelte NavHosts: Innerhalb eines composable-Bildschirms können Sie Ihren eigenen NavHost mit einem unabhängigen Routenstapel deklarieren.

Jeder verschachtelte NavHost hat seinen eigenen NavController und Back-Stack. Dies bedeutet, dass die Navigation innerhalb eines Tabs die Navigation in anderen Tabs nicht beeinflusst — der Benutzer kann frei zwischen Tabs wechseln, ohne die Navigationshistorie innerhalb eines jeden Tabs zu verlieren. Diese Architektur wird Scoped Navigation genannt und von Google für Anwendungen mit komplexer mehrstufiger Navigation empfohlen.

Bei der Implementierung verschachtelter Navigation ist es wichtig, den NavController-Zustand korrekt zu verwalten: Jeder verschachtelte NavHost sollte seinen eigenen rememberNavController innerhalb des Gültigkeitsbereichs der composable-Funktion speichern. Laut Android Developer Summit 2024 verwenden mehr als 40% der Jetpack Compose-Anwendungen mit drei oder mehr Tabs die Architektur verschachtelter NavHosts, um die Navigation zwischen Modulen zu isolieren.

kotlin
// Haupt-NavHost mit Tabs
composable("tabs") {
    MainTabsScreen { tab ->
        when (tab) {
            Tab.Home -> HomeNavGraph()
            Tab.Search -> SearchNavGraph()
        }
    }
}

// Verschachtelter Graph im Home-Tab
@Composable
fun HomeNavGraph() {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        startDestination = "home_feed"
    ) {
        composable("home_feed") { FeedScreen() }
        composable("home_detail/{postId}") { PostDetailScreen() }
    }
}

composable() vs. Intent-Navigation

Vor Jetpack Compose verwendete die Standardmethode der Navigation in Android Intent und FragmentManager. Intent ist eine Systemnachricht, die eine neue Activity startet, was die Neuerstellung des gesamten View-Baums bedeutet. Im Gegensatz dazu arbeitet composable() innerhalb einer einzigen Activity und ersetzt einfach einen Teil des Compose-Baums, was wesentlich schneller und speichereffizienter ist.

Hauptunterschiede zwischen composable() und Intent-basierter Navigation:

  • Geschwindigkeit — composable() wechselt Bildschirme in Millisekunden ohne Neuerstellung der Activity; Intent erfordert einen Neustart der Activity.
  • Animationen — in Navigation Compose werden Übergangsanimationen deklarativ über AnimatedNavHost definiert, ohne dass overridePendingTransition erforderlich ist.
  • Geteilter Zustand — composable() arbeitet in einem gemeinsamen ViewModel-Bereich, was die Datenübertragung zwischen Bildschirmen ohne Intent extras vereinfacht.
Eigenschaftcomposable()Intent / Fragment
ArchitekturSingle Activity, Compose-BaumMulti Activity, Fragment-Stapel
DatenübertragungPfad-/Abfrageparameter, gemeinsames ViewModelIntent extras, Bundle, SharedPreferences
Deep LinksIntegrierte navDeepLink-Unterstützungintent-filter im Manifest
Back-StackAutomatische popBackStack-VerwaltungFragmentManager.popBackStack()
Wechselzeit5–15 ms (prozessintern)50–200 ms (mit Neuerstellung)

Der Wechsel von Intent zu composable() ist nicht nur ein API-Austausch, sondern ein Wandel des Architekturparadigmas. Anstatt explizit anzugeben, welche Activity geöffnet werden soll, beschreibt der Entwickler alle möglichen Routen deklarativ an einem Ort, was die Lesbarkeit des Codes verbessert und das Testen der Navigation vereinfacht. Laut Google I/O 2024 reduziert Jetpack Compose mit Navigation Compose den Navigationscode um 40–60% im Vergleich zu FragmentManager.

Häufige Fehler mit composable()

Einer der häufigsten Fehler ist die Neuerstellung des NavController während der Rekomposition. Wenn NavController über rememberNavController() auf der Ebene des übergeordneten composable erstellt wird, das bei Zustandsänderungen neu erstellt werden kann, bricht die Navigation zusammen — die Historie geht verloren. Die richtige Lösung ist, NavController auf eine stabile composable-Ebene zu heben, wie die Activity-Ebene oder das Root-composable der Anwendung.

Das zweite häufige Problem ist die endlose Rekomposition während der Navigation. Dies tritt auf, wenn navController.navigate() direkt im Rumpf einer composable-Funktion platziert wird. Da die Navigation den Zustand des NavHost ändert, löst dies eine Rekomposition aus, die erneut navigate() aufruft, was eine Schleife erzeugt. Alle Navigationsaufrufe sollten in Lambda-Handler (onClick, onButtonPressed) eingebettet und nicht in der Komposition ausgeführt werden.

Der dritte Fehler ist die falsche Back-Stack-Behandlung bei Verwendung von BottomNavigation. Die einfache Navigation über navigate() bei jedem Tab-Wechsel fügt dem Stapel einen neuen Eintrag hinzu, anstatt zum vorhandenen zurückzukehren. Für BottomNavigation sollten Sie navController.navigate() mit restoreState = true und launchSingleTop = true verwenden, was die korrekte Zustandswiederherstellung beim Tab-Wechsel gewährleistet.

kotlin
fun NavController.navigateToTab(route: String) {
    navigate(route) {
        popUpTo(navController.graph.findStartDestination().id) {
            saveState = true
        }
        launchSingleTop = true
        restoreState = true
    }
}

Häufig gestellte Fragen

Was ist der Unterschied zwischen composable() und einer normalen @Composable-Funktion?

composable() ist keine Annotation, sondern eine Erweiterungsfunktion von NavHost, die eine Route an die UI bindet. Eine normale @Composable-Funktion beschreibt lediglich das Layout, während composable() dieses Layout mit einer bestimmten Route im Navigationsgraphen registriert und es so für die Navigation über NavController zugänglich macht.

Wie übergibt man ein komplexes Objekt zwischen composable()-Bildschirmen?

Es wird empfohlen, nur eine Kennung (ID) über den Pfadparameter zu übergeben und das Objekt auf dem Bildschirm anhand der ID über ein Repository oder ViewModel zu laden. Wenn Sie das Objekt dennoch übergeben müssen, verwenden Sie NavType.ParcelableType, vermeiden Sie jedoch die Übergabe von Objekten größer als 1 KB — dies kann zu einer TransactionTooLargeException führen.

Warum wird der composable()-Bildschirm bei Bildschirmdrehung neu erstellt?

Die Bildschirmdrehung löst eine Konfigurationsänderung aus, die standardmäßig die Activity neu erstellt. Um den Zustand von composable-Bildschirmen zu bewahren, verwenden Sie rememberSaveable für einfache Daten oder ViewModel mit dem Gültigkeitsbereich dieses Bildschirms. Navigation Compose stellt den Back-Stack nach der Neuerstellung wieder her, aber der Zustand innerhalb von composable()-Funktionen wird ohne rememberSaveable zurückgesetzt.

Kann composable() ohne NavHost verwendet werden?

Nein, composable() ist eine Erweiterungsfunktion von NavGraphBuilder, die nur innerhalb des NavHost-Blocks verfügbar ist. Für einfache UI-Ersetzung ohne Navigation verwenden Sie bedingtes Rendering (when, if) oder AnimatedContent. composable() ist speziell für das Routing mit Back-Stack- und Deep-Link-Unterstützung konzipiert.

Wie unterscheidet man die erste Navigation von einer Rückwärtsnavigation in composable()?

Verwenden Sie SavedStateHandle innerhalb des ViewModel: Bei der ersten Navigation gibt handle.get("initialized") null zurück; bei der Rückwärtsnavigation den gespeicherten Wert. Alternativ analysieren Sie die aktuelle Position im Back-Stack über navController.previousBackStackEntry — wenn sie null ist, ist dies der erste Bildschirm im Navigationsstapel.

Zusammenfassung

  • composable() ist eine Bildschirm-Registrierungsfunktion im NavHost, die wichtigste Methode zur Organisation der Navigation in Jetpack Compose.
  • Routen — jeder Bildschirm wird durch eine Routenzeichenfolge mit optionalen Pfad- und Abfrageparametern identifiziert.
  • Argumente — werden über NavArgument mit Unterstützung für primitive, Parcelable und Serializable Typen übergeben.
  • Verschachtelung — composable() unterstützt verschachtelte NavHosts für modulare Navigation mit unabhängigen Stapeln.
  • Leistung — Lazy-Bildschirminitialisierung spart Speicher, die Bildschirmwechselgeschwindigkeit beträgt 5–15 ms.
  • Fehler — Hauptprobleme: NavController-Neuerstellung, endlose Rekomposition mit navigate() im composable-Rumpf, falsche BottomNavigation-Behandlung.
  • Migration — der Wechsel von FragmentManager zu composable() reduziert den Navigationscode um 40–60% und eliminiert eine Klasse von Fehlern im Zusammenhang mit dem Fragment-Lebenszyklus.

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