NavHost este un container composable care servește ca punct de intrare pentru graful de navigație în Jetpack Compose. Acesta leagă NavController de un set de rute și afișează ecranul curent în funcție de starea back stack. Conform Android Developers (2025), NavHost este o componentă obligatorie pentru orice aplicație Compose cu navigație. În interiorul NavHost se înregistrează rute composable cu argumente opționale, deep links și animație. Fiecare rută este o funcție composable obișnuită care primește NavBackStackEntry cu datele tranziției. NavHost gestionează automat back press, salvarea stării și restaurarea la reconfigurare.
Principalele puncte
NavHost este o funcție composable care oferă un container pentru afișarea ecranului de navigare curent. NavHost primește NavController, startDestination și graful de rute construit prin Kotlin DSL. La schimbarea rutei curente, NavHost comută composable-ul afișat cu animația specificată.
NavHost funcționează ca un comutator de ecrane: urmărește NavBackStackEntry curent din NavController și afișează blocul composable corespunzător. Fiecare ecran este o funcție composable independentă care primește NavBackStackEntry cu argumentele rutei. Toate ecranele există într-un singur arbore de compoziție, dar NavHost arată doar unul odată, ascunzându-le pe celelalte prin animație.
Spre deosebire de FragmentManager, NavHost nu creează un Fragment pentru fiecare ecran. Întregul ciclu de viață este gestionat prin CompositionLifecycle — funcțiile composable nu au onStart/onResume, de aceea pentru efecte secundare se folosesc LaunchedEffect și DisposableEffect. NavHost se abonează automat la NavController și recompune UI la schimbarea rutei.
Conform Google, NavHost este un API stabil de la versiunea Navigation 2.4.0. Începând cu 2.8.0, NavHost suportă Type-Safe Navigation prin Kotlin Serialization, care înlocuiește route-urile string cu data-clase. NavHost suportă și grafuri imbricate, permițând organizarea navigației pe module.
NavHost se creează cu doi parametri obligatorii: navController (instanță NavHostController) și startDestination (șirul rutei primului ecran). Al treilea parametru — blocul builder, în care se înregistrează toate rutele prin composable(), navigation() și dialog().
@Composable
fun AppNavHost(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") { HomeScreen(navController) }
composable("settings") { SettingsScreen(navController) }
}
}
startDestination este ruta care se deschide la prima lansare a NavHost. Dacă back stack este gol, NavHost adaugă automat startDestination în stivă. La reconfigurare (rotirea ecranului), NavHost restaurează ultima rută din savedState, nu startDestination.
Pentru BottomNavigation, startDestination este una dintre rutele panoului inferior. Celelalte rute ale panoului se adaugă ca înregistrări composable separate. NavHost trebuie plasat în interiorul Scaffold.content — acolo unde se afișează conținutul principal al aplicației. NavHost ocupă întreaga înălțime disponibilă minus TopAppBar și BottomNavigation.
Funcția composable(route, arguments, deepLinks, enterTransition, exitTransition, content) înregistrează o rută în graful NavHost. Parametrul route este un șir care descrie calea cu placeholder-uri opționale de forma {paramName}. Placeholder-ul este înlocuit cu valoarea concretă la navigare.
Blocul content primește NavBackStackEntry din care se extrag argumentele. Funcția composable a ecranului se afișează doar când ruta curentă a NavController coincide cu route. La nepotrivire, composable este eliminat din compoziție, dar starea sa poate fi salvată prin rememberSaveable sau ViewModel cu 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)
}
Numărul înregistrărilor composable în NavHost poate fi oricât — de la câteva până la sute. Pentru aplicații mari, rutele se împart pe module și se conectează prin grafuri imbricate. Fiecare composable poate avea propriile setări de animație, deep links și argumente.
Argumentele rutei se definesc prin parametrul arguments: List<NamedNavArgument> în composable(). Fiecare argument se specifică prin navArgument(name) { type; defaultValue }. NavType determină tipul argumentului: StringType, IntType, LongType, FloatType, BoolType, ParcelableType și ReferenceType.
| Parametrul rutei | Exemplu route | NavType |
|---|---|---|
| Cale (path) | „user/{id}" | NavType.IntType |
| Interogare (query) | „search?q={query}" | NavType.StringType |
| Opțional | „details/{id}?tab={tab}" | StringType + defaultValue="" |
| Parcelable | „checkout/{order}" | NavType.ParcelableType |
Argumentele se extrag din NavBackStackEntry prin arguments?.getInt("id"). Pentru argumentele obligatorii, defaultValue poate lipsi — NavType va folosi null. Pentru argumentele opționale, defaultValue este obligatoriu, altfel navigarea va genera o excepție la lipsa parametrului.
De la versiunea Navigation 2.8.0 se recomandă Type-Safe Navigation: definiți o sealed class sau data class pentru rute cu Kotlin Serialization. În loc de route string, utilizați composable<RouteType> { backStackEntry -> }. Aceasta elimină greșelile de tipar în route și generează automat NavType pentru argumente. Pentru migrare, adăugați dependența navigation-compose-typesafe și plugin-ul Kotlin Serialization.
nested graphs — mecanism de grupare a rutelor în NavHost prin funcția navigation(route, startDestination). Un graf imbricat are propriul prefix de route și startDestination, iar toate rutele sale sunt accesibile prin prefix. nested graphs se folosesc în arhitectura modulară, unde fiecare modul feature își înregistrează subgraful propriu.
Avantajele grafurilor imbricate: izolarea rutelor în cadrul modulului, back stack unitar pentru grupul de ecrane, posibilitatea navigării după prefix fără a dezvălui structura internă. De exemplu, graful „auth" conține „auth/login" și „auth/register". Navigarea este posibilă atât pe ruta completă, cât și pe prefix cu redirecționare la 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 suportă transmiterea argumentelor la nivel de graf: parametrii declarați în route-ul grafului se transmit către toate rutele interne. Pentru curățarea grafului imbricat, utilizați popBackStack(route) — va șterge toate înregistrările interne. Grafurile imbricate nu au limită de adâncime, dar se recomandă maximum 3 niveluri pentru lizibilitate.
NavHost suportă animația tranzițiilor între rutele composable prin parametrii enterTransition, exitTransition, popEnterTransition și popExitTransition. Animațiile se definesc o dată pentru NavHost și se aplică tuturor rutelor, sau individual pentru fiecare composable. Implicit, animația este dezactivată.
Configurație tipică: enterTransition = slideInHorizontally(initialOffsetX = { it }) — ecranul intră din dreapta; exitTransition = slideOutHorizontally(targetOffsetX = { -it }) — ecranul iese în stânga. Pentru animația pop, direcțiile sunt în oglindă: ecranul intră din stânga și iese în dreapta. Pentru BottomNavigation se folosește fadeIn/fadeOut fără 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 */ }
Animațiile personalizate se creează prin Compose Animation API: AnimatedContentTransitionScope oferă acces la dimensiunile containerului, progresul animației și direction. Pentru shared element transition (un element care trece lin pe alt ecran) este necesară biblioteca Accompanist Navigation Animation sau o implementare personalizată prin sharedElement Modifier. Conform Android Developers (2025), animația slide implicită (intrare din dreapta, ieșire în stânga) este folosită în 80% din aplicațiile Android cu navigație.
Întrebări frecvente
Tehnic da, dar nu se recomandă. Fiecare NavHost creează un back stack independent, ceea ce încalcă navigarea unitară. Excepție — zone separate, de exemplu NavHost pentru conținutul principal și NavHost pentru BottomSheet cu navigare proprie.
NavHost — container de navigație care comută ecranele. Scaffold — aspectul întregii pagini (TopAppBar, BottomNavigation, FloatingActionButton). De obicei, NavHost este plasat în interiorul Scaffold.content. Scaffold nu gestionează navigarea, ci doar oferă sloturi pentru componentele UI.
ViewModel se creează în cadrul NavBackStackEntry prin viewModel(). Pentru partajarea ViewModel între ecrane, utilizați parentNavController: ViewModel partajat se leagă de entry-ul părinte. Alternativă — DI (Hilt/Koin) cu scope pe NavGraph.
Este un comportament normal — NavHost elimină composable-ul din compoziție la părăsirea rutei. Pentru păstrarea stării, utilizați rememberSaveable pentru starea UI și ViewModel cu SavedStateHandle pentru logica de business.
Adăugați ultima rută composable("404") și navigarea către ea la deep link necunoscut. În NavHost nu există rută catch-all — verificați ruta în intent-handler-ul Deep Link înainte de navigate(). Dacă route nu este găsit — navigați la 404.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și