NavHost — какво е това, изграждане на граф и маршрути в Jetpack Compose

Автор: IT Sectr Публикувано: 2026-06-30 Време за четене: 7 мин

NavHost е composable контейнер, който служи като входна точка за навигационния граф в Jetpack Compose. Той свързва NavController с набор от маршрути и визуализира текущия екран в зависимост от състоянието на back stack. Според Android Developers (2025), NavHost е задължителен компонент за всяко Compose приложение с навигация. Вътре в NavHost се регистрират composable маршрути с опционални аргументи, deep links и анимация. Всеки маршрут е обикновена composable функция, която получава NavBackStackEntry с данни за прехода. NavHost автоматично обработва back press, запазване на състоянието и възстановяване при реконфигурация.

Основни точки

  • NavHost — composable контейнер, свързващ NavController с графа на маршрути и визуализиращ текущия екран
  • composable() — функция за регистрация на маршрут с route, аргументи, deep links и анимация на преходи
  • startDestination — началният маршрут, отварян при създаване на NavHost
  • Аргументи се задават чрез navArgument с NavType за типизирано предаване на данни между екрани
  • Анимация се конфигурира глобално или индивидуално чрез параметрите enterTransition, exitTransition

Какво е NavHost в Jetpack Compose?

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().

kotlin
@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

Функцията composable(route, arguments, deepLinks, enterTransition, exitTransition, content) регистрира маршрут в графа на NavHost. Параметърът route е низ, описващ пътя с опционални placeholders във формата {paramName}. Placeholder се заменя с конкретна стойност при навигация.

Content блокът получава NavBackStackEntry, от който се извличат аргументите. Composable функцията на екрана се визуализира само когато текущият маршрут на NavController съвпада с route. При несъвпадение composable се премахва от композицията, но състоянието му може да бъде запазено чрез rememberSaveable или ViewModel със SavedStateHandle.

kotlin
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.

Параметър на маршрутПример за routeNavType
Път (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.

kotlin
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.

kotlin
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 в едно Activity?

Технически да, но не се препоръчва. Всеки NavHost създава независим back stack, което нарушава единната навигация. Изключение — отделни области, например NavHost за основно съдържание и NavHost за BottomSheet със собствена навигация.

Каква е разликата между NavHost и Scaffold в Compose?

NavHost — навигационен контейнер, превключващ екрани. Scaffold — оформление на цялата страница (TopAppBar, BottomNavigation, FloatingActionButton). Обикновено NavHost се поставя вътре в Scaffold.content. Scaffold не управлява навигация, а само предоставя слотове за UI компоненти.

Как да предам ViewModel между екрани чрез NavHost?

ViewModel се създава в рамките на NavBackStackEntry чрез viewModel(). За споделяне на ViewModel между екрани използвайте parentNavController: свържете споделения ViewModel към родителския entry. Алтернатива — DI (Hilt/Koin) с обхват NavGraph.

Защо NavHost пресъздава composable при всеки преход?

Това е нормално поведение — NavHost премахва composable от композицията при напускане на маршрута. За запазване на състоянието използвайте rememberSaveable за UI състояние и ViewModel със SavedStateHandle за бизнес логика.

Как да добавя обработка на 404 (неизвестен маршрут) в NavHost?

Добавете последния маршрут composable("404") и навигация към него при неизвестен deep link. В NavHost няма catch-all маршрут — проверявайте маршрута в intent handler на Deep Link преди navigate(). Ако route не е намерен — навигирайте към 404.

Обобщение

  • NavHost — composable контейнер на Navigation Compose, свързващ NavController с графа на маршрути и визуализиращ текущия екран
  • composable() регистрира маршрут с route, аргументи (NavType), deep links и анимация
  • startDestination — началният маршрут, отварян при първото стартиране на NavHost
  • Аргументи се предават чрез placeholders {param} с NavType за типизиране и defaultValue за опционални параметри
  • Вложени графи чрез navigation() позволяват групиране на маршрути по модули с изолиран back stack
  • Анимация на преходи се конфигурира чрез enterTransition/exitTransition с използване на Compose Animation API
  • NavHost автоматично обработва back press, запазване на състоянието и deep links без допълнителен код

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също