Навигация: NavHost, маршруты и аргументы

Связываем несколько экранов: создаём граф навигации через NavHost и передаём аргументы между маршрутами.

NavHost — контейнер, который по текущему маршруту показывает нужный экран, а NavController управляет переходами.

Граф экранов

Навигация в Compose описывается декларативно: вы перечисляете маршруты (строковые имена) и сопоставляете каждому composable-экран. NavHost показывает тот, что соответствует текущему маршруту:

@Composable
fun AppNav() {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = "home") {
        composable("home") {
            HomeScreen(onOpenDetails = { navController.navigate("details") })
        }
        composable("details") {
            DetailsScreen()
        }
    }
}

rememberNavController() создаёт и запоминает контроллер. startDestination — экран, который показывается первым. Переход выполняется методом navController.navigate("details").

Передача аргументов

Часто нужно открыть экран с конкретными данными — например, детали товара по его id. Аргумент встраивают в маршрут как часть пути:

NavHost(navController, startDestination = "list") {
    composable("list") {
        ListScreen(onItemClick = { id -> navController.navigate("item/$id") })
    }
    composable("item/{itemId}") { backStackEntry ->
        val id = backStackEntry.arguments?.getString("itemId")
        ItemScreen(itemId = id)
    }
}

Шаблон item/{itemId} объявляет параметр маршрута. При переходе navigate("item/42") значение «42» попадёт в backStackEntry.arguments под ключом itemId. Помимо обязательных аргументов в пути, бывают необязательные query-параметры (item?tab=info) и аргументы с типом и значением по умолчанию — их объявляют через список arguments у composable, указывая navArgument("itemId") { type = NavType.IntType }. Тип важен: тогда вы получите готовое число, а не строку.

Схема навигации

ListScreen --navigate("item/42")--> ItemScreen(itemId = "42")
     ^                                      |
     +-------- кнопка "Назад" --------------+

Как работает под капотом

NavController ведёт стек переходов (back stack). Каждый navigate кладёт новый экран наверх стека; системная кнопка «Назад» снимает верхний. NavHost наблюдает за вершиной стека и показывает соответствующий маршруту composable. Аргументы хранятся в записи стека (NavBackStackEntry), поэтому переживают перерисовку и возврат назад.

Частые ошибки

  • Передавать через маршрут крупные объекты целиком — в путь кладут только лёгкие идентификаторы (id), а данные подгружают по нему.
  • Создавать NavController без rememberNavController — он пересоздастся при перерисовке и потеряет стек.
  • Опечататься в имени маршрута — навигация молча не сработает, потому что имена сверяются как строки.

Итог

  • NavHost + NavController описывают граф экранов и управляют переходами.
  • Маршруты — строки; аргументы встраивают в путь как item/{itemId}.
  • Навигация работает через стек переходов; передавайте по маршруту id, а не большие объекты.
Проверьте себя
1. Как обычно передают аргумент между экранами в Navigation Compose?
AЧерез глобальную переменную
BВстраивают в маршрут, например item/{itemId}, и читают из backStackEntry
CЧерез файл на диске
DЭто невозможно
2. Почему NavController создают через rememberNavController?
AТак требует Material 3
BЧтобы контроллер сохранялся между перерисовками и не терял стек переходов
CДля ускорения навигации
DЧтобы включить тёмную тему
3. Что обычно передают через маршрут навигации?
AКрупные объекты целиком
BЛёгкие идентификаторы (id), а данные загружают по ним
CИзображения
DСостояние всего экрана