NavHost е composable контейнер, който служи като входна точка за навигационния граф в Jetpack Compose. Той свързва NavController с набор от маршрути и визуализира текущия екран в зависимост от състоянието на back stack. Според Android Developers (2025), NavHost е задължителен компонент за всяко Compose приложение с навигация. Вътре в NavHost се регистрират composable маршрути с опционални аргументи, deep links и анимация. Всеки маршрут е обикновена composable функция, която получава NavBackStackEntry с данни за прехода. NavHost автоматично обработва back press, запазване на състоянието и възстановяване при реконфигурация.
Основни точки
NavHost е composable функция, която предоставя контейнер за показване на текущия навигационен екран. NavHost приема NavController, startDestination и графа на маршрути, изграден чрез Kotlin DSL. При промяна на текущия маршрут NavHost превключва показания composable със зададена анимация.
NavHost работи като превключвател на екрани: той проследява текущия NavBackStackEntry от NavController и визуализира съответния composable блок. Всеки екран е независима composable функция, която получава NavBackStackEntry с аргументи на маршрута. Всички екрани съществуват в единно дърво на композицията, но NavHost показва само един наведнъж, скривайки останалите чрез анимация.
За разлика от FragmentManager, NavHost не създава Fragment за всеки екран. Целият жизнен цикъл се управлява чрез CompositionLifecycle — composable функциите нямат onStart/onResume, затова за странични ефекти се използват LaunchedEffect и DisposableEffect. NavHost автоматично се абонира за NavController и прекомпозира UI при промяна на маршрута.
Според Google, NavHost е стабилен API от версия Navigation 2.4.0. От версия 2.8.0 NavHost поддържа Type-Safe Navigation чрез Kotlin Serialization, което заменя строковите route с data класове. NavHost също поддържа вложени графи, което позволява организиране на навигация по модули.
NavHost се създава с два задължителни параметъра: navController (екземпляр на NavHostController) и startDestination (низ на маршрута на първия екран). Третият параметър е builder блок, в който се регистрират всички маршрути чрез composable(), navigation() и dialog().
@Composable
fun AppNavHost(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") { HomeScreen(navController) }
composable("settings") { SettingsScreen(navController) }
}
}
startDestination е маршрутът, който се отваря при първото стартиране на NavHost. Ако back stack е празен, NavHost автоматично добавя startDestination в стека. При реконфигурация (завъртане на екрана) NavHost възстановява последния маршрут от savedState, а не startDestination.
За BottomNavigation startDestination е един от маршрутите на долния панел. Останалите маршрути на панела се добавят като отделни composable записи. NavHost трябва да бъде поставен вътре в Scaffold.content — там, където се показва основното съдържание на приложението. NavHost заема цялата налична височина минус TopAppBar и BottomNavigation.
Функцията composable(route, arguments, deepLinks, enterTransition, exitTransition, content) регистрира маршрут в графа на NavHost. Параметърът route е низ, описващ пътя с опционални placeholders във формата {paramName}. Placeholder се заменя с конкретна стойност при навигация.
Content блокът получава NavBackStackEntry, от който се извличат аргументите. Composable функцията на екрана се визуализира само когато текущият маршрут на NavController съвпада с route. При несъвпадение composable се премахва от композицията, но състоянието му може да бъде запазено чрез rememberSaveable или ViewModel със 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)
}
Броят на composable записите вътре в NavHost може да бъде всякакъв — от няколко до стотици. За големи приложения маршрутите се разделят по модули и се свързват чрез вложени графи. Всеки composable може да има собствени настройки за анимация, deep links и аргументи.
Аргументите на маршрута се определят чрез параметъра arguments: List<NamedNavArgument> в composable(). Всеки аргумент се задава чрез navArgument(name) { type; defaultValue }. NavType определя типа на аргумента: StringType, IntType, LongType, FloatType, BoolType, ParcelableType и ReferenceType.
| Параметър на маршрут | Пример за route | NavType |
|---|---|---|
| Път (path) | „user/{id}" | NavType.IntType |
| Заявка (query) | „search?q={query}" | NavType.StringType |
| Опционален | „details/{id}?tab={tab}" | StringType + defaultValue="" |
| Parcelable | „checkout/{order}" | NavType.ParcelableType |
Аргументите се извличат от NavBackStackEntry чрез arguments?.getInt("id"). За задължителни аргументи defaultValue може да отсъства — NavType ще използва null. За опционални аргументи defaultValue е задължителна, в противен случай навигацията ще хвърли изключение при липса на параметъра.
От версия Navigation 2.8.0 се препоръчва Type-Safe Navigation: дефинирайте sealed class или data class за маршрути с Kotlin Serialization. Вместо строков route използвайте composable<RouteType> { backStackEntry -> }. Това елиминира правописни грешки в route и автоматично генерира NavType за аргументи. За миграция добавете зависимостта navigation-compose-typesafe и приставката Kotlin Serialization.
nested graphs — механизъм за групиране на маршрути вътре в NavHost чрез функцията navigation(route, startDestination). Вложеният граф има собствен префикс на route и startDestination, а всички негови маршрути са достъпни чрез префикса. nested graphs се използват за модулна архитектура, където всеки feature модул регистрира свой подграф.
Предимства на вложените графи: изолация на маршрути вътре в модула, единен back stack за група екрани, възможност за навигация по префикс без разкриване на вътрешната структура. Например графът „auth" съдържа „auth/login" и „auth/register". Навигацията е възможна както по пълния маршрут, така и по префикс с пренасочване към 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 поддържат предаване на аргументи на ниво граф: параметрите, декларирани в route на графа, се предават на всички вътрешни маршрути. За почистване на вложен граф използвайте popBackStack(route) — ще премахне всички вътрешни записи. Вложените графи нямат ограничение за дълбочина, но за четливост се препоръчват не повече от 3 нива.
NavHost поддържа анимация на преходи между composable маршрути чрез параметрите enterTransition, exitTransition, popEnterTransition и popExitTransition. Анимациите се дефинират веднъж за NavHost и се прилагат към всички маршрути, или индивидуално за всеки composable. По подразбиране анимацията е изключена.
Типична конфигурация: enterTransition = slideInHorizontally(initialOffsetX = { it }) — екранът влиза отдясно; exitTransition = slideOutHorizontally(targetOffsetX = { -it }) — екранът излиза наляво. За pop анимация посоките са огледални: екранът влиза отляво и излиза надясно. За BottomNavigation се използва fadeIn/fadeOut без 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 */ }
Персонализирани анимации се създават чрез Compose Animation API: AnimatedContentTransitionScope предоставя достъп до размерите на контейнера, напредъка на анимацията и direction. За shared element transition (един елемент плавно преминава към друг екран) е необходима библиотеката Accompanist Navigation Animation или персонализирана реализация чрез sharedElement Modifier. Според Android Developers (2025), анимацията slide по подразбиране (вход отдясно, изход наляво) се използва в 80% от Android приложенията с навигация.
Често задавани въпроси
Технически да, но не се препоръчва. Всеки NavHost създава независим back stack, което нарушава единната навигация. Изключение — отделни области, например NavHost за основно съдържание и NavHost за BottomSheet със собствена навигация.
NavHost — навигационен контейнер, превключващ екрани. Scaffold — оформление на цялата страница (TopAppBar, BottomNavigation, FloatingActionButton). Обикновено NavHost се поставя вътре в Scaffold.content. Scaffold не управлява навигация, а само предоставя слотове за UI компоненти.
ViewModel се създава в рамките на NavBackStackEntry чрез viewModel(). За споделяне на ViewModel между екрани използвайте parentNavController: свържете споделения ViewModel към родителския entry. Алтернатива — DI (Hilt/Koin) с обхват NavGraph.
Това е нормално поведение — NavHost премахва composable от композицията при напускане на маршрута. За запазване на състоянието използвайте rememberSaveable за UI състояние и ViewModel със SavedStateHandle за бизнес логика.
Добавете последния маршрут composable("404") и навигация към него при неизвестен deep link. В NavHost няма catch-all маршрут — проверявайте маршрута в intent handler на Deep Link преди navigate(). Ако route не е намерен — навигирайте към 404.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също