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 для каждого экрана. Весь lifecycle управляется через CompositionLifecycle — у composable-функций нет onStart/onResume, поэтому для side-эффектов используются LaunchedEffect и DisposableEffect. NavHost автоматически подписывается на NavController и перекомпоновывает UI при изменении маршрута.
По данным Google, NavHost является stable 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 — это строка, описывающая путь с опциональными плейсхолдерами вида {paramName}. Плейсхолдер заменяется на конкретное значение при навигации.
Content-блок composable получает 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 без слайда.
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-анимация по умолчанию (enter справа, exit влево) используется в 80% Android-приложений с навигацией.
Часто задаваемые вопросы
Технически да, но не рекомендуется. Каждый NavHost создаёт независимый back stack, что нарушает единую навигацию. Исключение — раздельные области, например, NavHost для основного контента и NavHost для BottomSheet с собственной навигацией.
NavHost — контейнер навигации, переключающий экраны. Scaffold — макет всей страницы (TopAppBar, BottomNavigation, FloatingActionButton). Обычно NavHost размещается внутри Scaffold.content. Scaffold не управляет навигацией, а только предоставляет слоты для UI-компонентов.
ViewModel создаётся в рамках NavBackStackEntry через viewModel(). Для шаринга ViewModel между экранами используйте parentNavController: shared ViewModel привяжите к родительскому entry. Альтернатива — DI (Hilt/Koin) с scope на NavGraph.
Это нормальное поведение — NavHost удаляет composable из композиции при уходе с маршрута. Для сохранения состояния используйте rememberSaveable для UI-состояния и ViewModel с SavedStateHandle для бизнес-логики.
Добавьте последний маршрут composable("404") и навигацию на него при неизвестном deep link. В NavHost нет catch-all маршрута — проверяйте маршрут в intent-обработчике Deep Link перед navigate(). Если route не найден — navigate на 404.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также