NavHost to kontener composable, który służy jako punkt wejścia dla grafu nawigacyjnego w Jetpack Compose. Łączy NavController z zestawem tras i renderuje bieżący ekran w zależności od stanu back stack. Według Android Developers (2025), NavHost jest obowiązkowym komponentem każdej aplikacji Compose z nawigacją. Wewnątrz NavHost rejestrowane są trasy composable z opcjonalnymi argumentami, deep links i animacją. Każda trasa to zwykła funkcja composable, która otrzymuje NavBackStackEntry z danymi przejścia. NavHost automatycznie obsługuje back press, zapisywanie stanu i przywracanie przy rekonfiguracji.
Najważniejsze
NavHost — to funkcja composable zapewniająca kontener do wyświetlania bieżącego ekranu nawigacji. NavHost przyjmuje NavController, startDestination i graf tras zbudowany przez Kotlin DSL. Po zmianie bieżącej trasy NavHost przełącza wyświetlany composable z zadaną animacją.
NavHost działa jak przełącznik ekranów: śledzi bieżący NavBackStackEntry z NavController i renderuje odpowiedni blok composable. Każdy ekran to niezależna funkcja composable, która otrzymuje NavBackStackEntry z argumentami trasy. Wszystkie ekrany istnieją w jednym drzewie kompozycji, ale NavHost pokazuje tylko jeden na raz, ukrywając pozostałe przez animację.
W przeciwieństwie do FragmentManager, NavHost nie tworzy Fragment dla każdego ekranu. Cały lifecycle jest zarządzany przez CompositionLifecycle — funkcje composable nie mają onStart/onResume, dlatego do side-efektów używa się LaunchedEffect i DisposableEffect. NavHost automatycznie subskrybuje NavController i przebudowuje UI przy zmianie trasy.
Według Google, NavHost jest stable API od wersji Navigation 2.4.0. Od wersji 2.8.0 NavHost obsługuje Type-Safe Navigation przez Kotlin Serialization, co zastępuje stringowe route na data-klasy. NavHost obsługuje również zagnieżdżone grafy, co pozwala organizować nawigację po modułach.
NavHost jest tworzony z dwoma obowiązkowymi parametrami: navController (instancja NavHostController) i startDestination (string trasy pierwszego ekranu). Trzeci parametr — builder-block, w którym rejestruje się wszystkie trasy przez composable(), navigation() i dialog().
@Composable
fun AppNavHost(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") { HomeScreen(navController) }
composable("settings") { SettingsScreen(navController) }
}
}
startDestination — to trasa otwierana przy pierwszym uruchomieniu NavHost. Jeśli back stack jest pusty, NavHost automatycznie dodaje startDestination do stosu. Przy rekonfiguracji (obrót ekranu) NavHost przywraca ostatnią trasę z savedState, a nie startDestination.
Dla BottomNavigation startDestination to jedna z tras dolnego panelu. Pozostałe trasy panelu są dodawane jako osobne wpisy composable. NavHost powinien być umieszczony wewnątrz Scaffold.content — tam, gdzie wyświetlana jest główna treść aplikacji. NavHost zajmuje całą dostępną wysokość pomniejszoną o TopAppBar i BottomNavigation.
Funkcja composable(route, arguments, deepLinks, enterTransition, exitTransition, content) rejestruje trasę w grafie NavHost. Parametr route to string opisujący ścieżkę z opcjonalnymi placeholderami w postaci {paramName}. Placeholder jest zastępowany konkretną wartością podczas nawigacji.
Content-block composable otrzymuje NavBackStackEntry, z którego wydobywane są argumenty. Funkcja composable ekranu jest renderowana tylko wtedy, gdy bieżąca trasa NavController pasuje do route. W przypadku niezgodności composable jest usuwany z kompozycji, ale jego stan może być zachowany przez rememberSaveable lub ViewModel z 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)
}
Liczba wpisów composable wewnątrz NavHost może być dowolna — od kilku do setek. Dla dużych aplikacji trasy są dzielone na moduły i podłączane przez zagnieżdżone grafy. Każdy composable może mieć własne ustawienia animacji, deep links i argumentów.
Argumenty trasy są definiowane przez parametr arguments: List<NamedNavArgument> w composable(). Każdy argument jest określany przez navArgument(name) { type; defaultValue }. NavType określa typ argumentu: StringType, IntType, LongType, FloatType, BoolType, ParcelableType i ReferenceType.
| Parametr trasy | Przykład route | NavType |
|---|---|---|
| Ścieżka (path) | „user/{id}" | NavType.IntType |
| Zapytanie (query) | „search?q={query}" | NavType.StringType |
| Opcjonalny | „details/{id}?tab={tab}" | StringType + defaultValue="" |
| Parcelable | „checkout/{order}" | NavType.ParcelableType |
Argumenty są wydobywane z NavBackStackEntry przez arguments?.getInt("id"). Dla obowiązkowych argumentów defaultValue może być pominięty — NavType będzie używać null. Dla opcjonalnych argumentów defaultValue jest wymagany, w przeciwnym razie nawigacja wyrzuci wyjątek przy braku parametru.
Od wersji Navigation 2.8.0 zalecany jest Type-Safe Navigation: zdefiniuj sealed class lub data class dla tras z Kotlin Serialization. Zamiast stringowego route używaj composable<RouteType> { backStackEntry -> }. Eliminuje to literówki w route i automatycznie generuje NavType dla argumentów. Do migracji dodaj zależność navigation-compose-typesafe i plugin Kotlin Serialization.
nested graphs — mechanizm grupowania tras wewnątrz NavHost przez funkcję navigation(route, startDestination). Zagnieżdżony graf ma własny prefiks route i startDestination, a wszystkie jego trasy są dostępne przez prefiks. nested graphs są używane w architekturze modułowej, gdzie każdy moduł feature rejestruje swój podgraf.
Zalety zagnieżdżonych grafów: izolacja tras wewnątrz modułu, jednolity back stack dla grupy ekranów, możliwość nawigacji po prefiksie bez ujawniania wewnętrznej struktury. Na przykład graf „auth" zawiera „auth/login" i „auth/register". Nawigacja jest możliwa zarówno po pełnej trasie, jak i po prefiksie z przekierowaniem na 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) }
}
}
nested graphs obsługują przekazywanie argumentów na poziomie grafu: parametry zadeklarowane w route grafu są przekazywane do wszystkich wewnętrznych tras. Do czyszczenia zagnieżdżonego grafu użyj popBackStack(route) — usunie wszystkie wewnętrzne wpisy. Zagnieżdżone grafy nie mają ograniczenia głębokości, ale zaleca się nie więcej niż 3 poziomy dla czytelności.
NavHost obsługuje animację przejść między trasami composable przez parametry enterTransition, exitTransition, popEnterTransition i popExitTransition. Animacje są definiowane raz dla NavHost i stosowane do wszystkich tras, albo indywidualnie dla każdego composable. Domyślnie animacja jest wyłączona.
Typowa konfiguracja: enterTransition = slideInHorizontally(initialOffsetX = { it }) — ekran wjeżdża z prawej; exitTransition = slideOutHorizontally(targetOffsetX = { -it }) — ekran wyjeżdża w lewo. Dla animacji pop kierunki są lustrzane: ekran wjeżdża z lewej i wyjeżdża w prawo. Dla BottomNavigation używane jest fadeIn/fadeOut bez slide.
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 */ }
Niestandardowe animacje są tworzone przez Compose Animation API: AnimatedContentTransitionScope zapewnia dostęp do rozmiarów kontenera, postępu animacji i direction. Dla shared element transition (jeden element płynnie przechodzi na inny ekran) wymagana jest biblioteka Accompanist Navigation Animation lub niestandardowa implementacja przez sharedElement Modifier. Według Android Developers (2025), animacja slide domyślnie (wjazd z prawej, wyjazd w lewo) jest używana w 80% aplikacji Android z nawigacją.
Często zadawane pytania
Technicznie tak, ale nie jest zalecane. Każdy NavHost tworzy niezależny back stack, co narusza jednolitą nawigację. Wyjątkiem są oddzielne obszary, np. NavHost dla głównej treści i NavHost dla BottomSheet z własną nawigacją.
NavHost — kontener nawigacji przełączający ekrany. Scaffold — układ całej strony (TopAppBar, BottomNavigation, FloatingActionButton). Zazwyczaj NavHost jest umieszczany wewnątrz Scaffold.content. Scaffold nie zarządza nawigacją, a jedynie udostępnia sloty dla komponentów UI.
ViewModel jest tworzony w ramach NavBackStackEntry przez viewModel(). Do współdzielenia ViewModel między ekranami użyj parentNavController: shared ViewModel podłącz do nadrzędnego entry. Alternatywą jest DI (Hilt/Koin) z zakresem NavGraph.
To normalne zachowanie — NavHost usuwa composable z kompozycji przy opuszczeniu trasy. Do zachowania stanu używaj rememberSaveable dla stanu UI i ViewModel z SavedStateHandle dla logiki biznesowej.
Dodaj ostatnią trasę composable("404") i nawigację do niej przy nieznanym deep link. W NavHost nie ma catch-all trasy — sprawdzaj trasę w intent-handlerze Deep Link przed navigate(). Jeśli route nie znaleziono — navigate na 404.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również