В мире Android-разработки «адаптивная вёрстка» (Adaptive Layout) перестала быть роскошью — теперь это необходимость. С появлением складных устройств и девайсов с большими экранами приложениям уже недостаточно просто растягиваться — им нужно перестраивать свою структуру UI.
Традиционно реализация сценариев вроде List-Detail или Supporting Pane требовала большого количества бойлерплейта: проверки классов window size, ручное управление back stack и обработка условной логики интерфейса. Nav3 — современный подход к навигации от Google — предлагает новый способ моделирования адаптивного UI через метаданные навигации.
Настройка: адаптивность «из коробки»
Основой современного адаптивного приложения является NavigationSuiteScaffold. Он автоматически переключается между BottomNavigationBar на компактных экранах и NavigationRail на более широких.
В своём проекте я использовал стандартные navigationState и реализацию Navigator, как рекомендуется в документации Google, чтобы сохранить основную логику чистой и предсказуемой.
val navigationState = rememberNavigationState(
startKey = HomeKey,
topLevelKeys = setOf(HomeKey, SearchKey, ProfileKey),
)
val navigator = remember(navigationState) { Navigator(navigationState) }
NavigationSuiteScaffold(
navigationSuiteItems = {
TopDestination.entries.forEach { dest ->
item(
selected = navigationState.currentTopLevelKey == dest.key,
onClick = { navigator.navigate(dest.key) },
icon = { Icon(dest.icon, contentDescription = dest.label) },
label = { Text(dest.label) },
)
}
}
) {
// Scaffold and NavDisplay go here
}
Перед тем как переходить к панелям, важно разобраться с контейнером верхнего уровня навигации. В этом проекте я использовал NavigationSuiteScaffold, который даёт готовое «из коробки» решение для адаптивной навигации.
Вместо того чтобы вручную проверять размеры экрана, scaffold сам использует текущую информацию об адаптивности окна (window adaptive info) и переключается между BottomNavigationBar для компактных экранов и NavigationRail для расширенных макетов (например, планшетов или разложенных foldable-устройств). Это гарантирует, что основная навигация остаётся удобной и доступной вне зависимости от того, как пользователь держит или разворачивает устройство.
Ядро: навигационные метаданные как драйвер layout’а
Самая мощная фича Nav3 от Google — возможность прикреплять метаданные к элементу навигации (navigation entry). Вместо того чтобы хардкодить условия вроде «если экран широкий — показываем detail», мы просто указываем, какой «pane» представляет данный экран.
Стоит учитывать, что эти API всё ещё развиваются и могут меняться по мере развития Nav3.
1. Симфония из трёх панелей: List, Detail и Extra
Большинство адаптивных приложений ограничиваются паттерном List-Detail. Однако современные сценарии многозадачности часто требуют третьего уровня — Extra Pane.
Именно здесь метаданные Nav3 раскрываются на полную: определяя listPane(), detailPane() и extraPane(), мы создаём иерархию экранов, которую навигационная система может гибко оркестрировать в зависимости от доступного пространства на экране.
entry<ListKey>(
metadata = listPane(detailPlaceholder = {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(
"Select an item",
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
})
) {
ListScreen(
onItemClick = { navigator.navigate(DetailKey(it)) },
onBack = { navigator.goBack() },
)
}
entry<DetailKey>(metadata = detailPane()) { key ->
DetailScreen(
key.itemId,
onBack = { navigator.goBack() },
onOpenExtra = { navigator.navigate(ExtraInfoKey(key.itemId)) },
)
}
entry<ExtraInfoKey>(metadata = extraPane()) { key ->
ExtraInfoScreen(key.itemId, onBack = { navigator.goBack() })
}
Как это выглядит на практике:
- Компактные экраны: опыт остаётся линейным. Переход от List к Detail, а затем к Extra Info ощущается как обычный стек из трёх экранов.
- Foldable-устройства и планшеты: здесь включается логика «скользящего окна». При переходе от List к Detail они отображаются рядом. Однако при открытии Extra Info scaffold смещает фокус: панель Detail уезжает влево (фактически заменяя List), а справа открывается Extra Pane.
Такое поведение гарантирует, что наиболее релевантный контент (элемент, который вы просматриваете, и его дополнительные детали) всегда находится в центре внимания, при этом навигация остаётся плавной и логически связанной через back stack в Nav3.
2. За пределами стандартных паттернов: Supporting Pane для медиа
Адаптивный дизайн — это не только про списки. Иногда нужен Supporting Pane — например, комментарии под видео или метаданные рядом с плеером. Nav3 позволяет комбинировать разные стратегии в рамках одного приложения.
entry<VideoKey>(metadata = mainPane() + preferredPaneSize(height = 0.4f)) {
VideoScreen(
onOpenComments = { navigator.navigate(CommentsKey) },
onBack = { navigator.goBack() },
)
}
entry<CommentsKey>(metadata = supportingPane()) {
CommentsScreen(onBack = { navigator.goBack() })
}
Комбинируя mainPane() с preferredPaneSize, мы даём layout-движку подсказку, как сбалансировать пространство на экране. Когда CommentsKey попадает в стек, метаданные supportingPane() сигнализируют системе, что его нужно показать рядом с видео, а не перекрывать его.
Как это выглядит на практике:
- Компактные экраны: пространство ограничено. Здесь
supportingPaneведёт себя адаптивно: при переходе к комментариям они могут отображаться под основным контентом, сохраняя фокус на медиа и обеспечивая быстрый доступ к обсуждению. - Foldable-устройства и планшеты:
NavDisplayраспознаётsupportingPane()и размещает его рядом с видео. Это позволяет одновременно смотреть контент и читать комментарии.
Используя preferredPaneSize(height = 0.4f), вы обеспечиваете сохранение пропорций и видимости видео, в то время как вспомогательный контент адаптируется под доступное пространство.
3. Оркестрация UI с помощью Scene Strategies
Все эти метаданные были бы бесполезны без механизма, который их интерпретирует. Здесь в игру вступают NavDisplay и Scene Strategies.
В NavDisplay мы комбинируем разные стратегии, чтобы одновременно обрабатывать как сценарии List-Detail, так и поведение Supporting Pane.
NavDisplay(
entries = navigationState.toEntries(mainEntryProvider),
modifier = Modifier.padding(innerPadding),
sceneStrategy = rememberListDetailSceneStrategy<NavKey>() then
rememberSupportingPaneSceneStrategy(shouldHandleSinglePaneLayout = true),
onBack = { navigator.goBack() },
)
Оператор then позволяет нам цепочкой объединять стратегии. NavDisplay анализирует верхние элементы вашего навигационного стека, читает их метаданные (listPane, detailPane, supportingPane) и передаёт управление стратегиям, которые решают, как в итоге отрендерить адаптивный UI.
Преимущества подхода Nav3
В завершение стоит подчеркнуть, почему декларативный подход лучше того, что было раньше:
- Разделение логики: UI больше не должен знать о размере экрана. Он просто знает, что он
detailPane. Всё остальное — измерение и размещение панелей — берёт на себяSceneStrategy. - Type-safe навигация: Больше никаких строковых роутов с кучей аргументов. Использование объектов/классов Kotlin для
NavKeyделает поток данных предсказуемым и безопасным. - Встроенное управление back stack: Даже при сложных «скользящих» переходах между тремя панелями
navigator.goBack()работает ожидаемо — например, возвращает экран из состояния[Detail | Extra]обратно к[List | Detail].
Заключение
Navigation 3 — это серьёзный шаг в сторону по-настоящему декларативной навигации в Jetpack Compose от Google. Перенося ответственность за оркестрацию макета с UI-экранов на навигационные метаданные, мы получаем гораздо более чистое разделение ответственности.
Теперь экраны не заботятся о том, отображаются ли они в single-pane или multi-pane окружении — они просто рендерят свой контент, а Nav3 отвечает за «как» и «где» его показать.
Рассмотренные паттерны — от «скользящего» трёхпанельного интерфейса до адаптивных supporting panes — показывают, что Nav3 — это не просто про навигацию между экранами, а полноценный фреймворк для создания современных приложений, нативно чувствующих себя на любом форм-факторе.
Исходный код
Если хотите посмотреть, как Navigation 3 и Material 3 Adaptive работают вместе в реальном проекте, изучите пример на GitHub.
Рекомендую склонировать репозиторий, запустить его на эмуляторе foldable-устройства и поэкспериментировать, добавляя собственные layout’ы на основе метаданных.

