Spec-Zone.ru › Kotlin 2

Navigation 3 в Compose Multiplatform

Библиотека Navigation для Android обновилась до Navigation 3. В ней представлен переработанный подход к навигации, который работает с Compose и учитывает отзывы о предыдущей версии библиотеки. Начиная с версии 1.10, Compose Multiplatform поддерживает использование Navigation 3 в мультиплатформенных проектах на всех поддерживаемых платформах: Android, iOS, настольных платформах и в вебе.

Основные понятия навигации см. в разделе Навигация в Compose.

Основные изменения

Navigation 3 — это не просто новая версия библиотеки: во многих отношениях это совершенно новая библиотека. Подробнее о принципах, лежащих в основе этой переработки, читайте в публикации в блоге Android Developers.

К основным изменениям в Navigation 3 относятся:

  • Стек возврата под управлением пользователя. Вместо использования единого стека возврата библиотеки вы создаёте и управляете SnapshotStateList состояний, за которым интерфейс наблюдает напрямую.

  • Низкоуровневые строительные блоки. Благодаря более тесной интеграции с Compose библиотека предоставляет больше возможностей для реализации собственных компонентов и поведения навигации.

  • Адаптивная система компоновки. Адаптивный дизайн позволяет одновременно отображать несколько пунктов назначения и плавно переключаться между вариантами компоновки.

Подробнее об общем устройстве Navigation 3 см. в документации Android.

Настройка зависимостей

Чтобы попробовать мультиплатформенную реализацию Navigation 3, добавьте следующую зависимость в каталог версий:

[versions]
multiplatform-nav3-ui = "1.1.1"

[libraries]
jetbrains-navigation3-ui = { module = "org.jetbrains.androidx.navigation3:navigation3-ui", version.ref = "multiplatform-nav3-ui" }

Хотя Navigation 3 выпускается в виде двух артефактов — navigation3:navigation3-ui и navigation3:navigation3-common, — отдельная реализация Compose Multiplatform есть только у navigation3-ui. Зависимость от navigation3-common добавляется транзитивно.

Если проект использует библиотеки Material 3 Adaptive и ViewModel, добавьте также следующие артефакты поддержки навигации:

[versions]
compose-multiplatform-adaptive = "1.3.0-beta02"
compose-multiplatform-lifecycle = "2.10.0"

[libraries]
jetbrains-material3-adaptiveNavigation3 = { module = "org.jetbrains.compose.material3.adaptive:adaptive-navigation3", version.ref = "compose-multiplatform-adaptive" }
jetbrains-lifecycle-viewmodelNavigation3 = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "compose-multiplatform-lifecycle" }

Наконец, вы можете попробовать библиотеку для проверки концепции, созданную инженером JetBrains. Она интегрирует мультиплатформенную Navigation 3 с навигацией по истории браузера в вебе:

[versions]
compose-multiplatform-navigation3-browser = "1.1.0"

[libraries]
navigation3-browser = { module = "com.github.terrakok:navigation3-browser", version.ref = "compose-multiplatform-navigation3-browser" }

Поддержка навигации по истории браузера в базовой мультиплатформенной библиотеке Navigation 3 запланирована для будущего выпуска. Следите за ходом работы в задаче CMP-8924.

Мультиплатформенная поддержка

Navigation 3 тесно связана с Compose, поэтому Android-реализацию навигации можно использовать в общем коде Compose Multiplatform с минимальными изменениями. Для поддержки платформ без JVM, таких как веб и iOS, необходимо реализовать полиморфную сериализацию ключей пунктов назначения.

На GitHub можно сравнить подробные примеры Android-приложений и мультиплатформенных приложений, использующих Navigation 3:

  • исходный репозиторий Android с примерами Navigation 3

  • проект Compose Multiplatform с большинством тех же примеров

Полиморфная сериализация ключей пунктов назначения

На Android Navigation 3 использует сериализацию на основе рефлексии, которая недоступна для платформ без JVM, например iOS. Чтобы обойти это ограничение, в библиотеке есть две перегрузки функции rememberNavBackStack():

  • Первая перегрузка принимает только набор ссылок NavKey и требует сериализатор на основе рефлексии.

  • Вторая перегрузка также принимает параметр SavedStateConfiguration, который позволяет указать SerializersModule и обрабатывать открытую полиморфную сериализацию на всех платформах.

Мультиплатформенные примеры Navigation 3 объявляют маршруты и регистрируют их с помощью SavedStateConfiguration, как показано ниже:

@Serializable
private data object RouteA : NavKey

@Serializable
private data class RouteB(val id: String) : NavKey

// Creates the required serialization configuration for open polymorphism
private val config = SavedStateConfiguration {
    serializersModule = SerializersModule {
        polymorphic(NavKey::class) {
            subclass(RouteA::class, RouteA.serializer())
            subclass(RouteB::class, RouteB.serializer())
        }
    }
}

@Composable
fun BasicDslActivity() {
    // Consumes the serialization configuration
    val backStack = rememberNavBackStack(config, RouteA)

    NavDisplay(
        backStack = backStack,
        //...
    )
}

Рекомендуемые подходы к сериализации

При реализации мультиплатформенной навигации необходимо выбрать, как организовать и сериализовать определения маршрутов. В зависимости от сложности проекта и его модульной структуры используйте один из следующих трёх шаблонов.

Один модуль с запечатанным типом

Для небольших проектов, в которых все маршруты находятся в одном модуле, используйте sealed interface. Это самый простой подход, поскольку Kotlin Serialization автоматически обрабатывает иерархию:

@Serializable
sealed interface Route : NavKey

@Serializable
data object RouteA : Route

@Serializable
data class RouteB(val id: String) : Route

// Backstack with default serializer
val backStack: MutableList<Route> =
    rememberSerializable(serializer = SnapshotStateListSerializer()) {
        mutableStateListOf(RouteA)
    }

Также можно явно использовать функцию rememberNavBackStack(). В таком случае конфигурация будет немного отличаться:

private val config = SavedStateConfiguration {
    serializersModule = SerializersModule {
        polymorphic(NavKey::class) {
            subclassesOfSealed<Route>()
        }
    }
}
val backStack = rememberNavBackStack(config, RouteA)

Несколько модулей с объединёнными запечатанными типами

В более сложных проектах, где маршруты определены в нескольких модулях, можно определить запечатанный тип для каждого модуля. Затем объедините сериализаторы в модуле app с помощью функции subclassesOfSealed().

// Module A
@Serializable sealed interface FeatureA : NavKey
@Serializable data object RouteA1 : FeatureA
@Serializable data object RouteA2 : FeatureA

// Module B
@Serializable sealed interface FeatureB : NavKey
@Serializable data class RouteB1(val id: String) : FeatureB
@Serializable data class RouteB2(val id: String) : FeatureB

// Module app
private val config = SavedStateConfiguration {
    serializersModule = SerializersModule {
        polymorphic(NavKey::class) {
            subclassesOfSealed<FeatureA>()
            subclassesOfSealed<FeatureB>()
        }
    }
}
val backStack = rememberNavBackStack(config, RouteA1)

При использовании внедрения зависимостей (DI) можно также использовать контейнеры DI, чтобы динамически собирать сериализаторы запечатанных типов из каждого модуля в Set<KSerializer>.

Несколько модулей с отдельной регистрацией маршрутов

Если маршруты нельзя сгруппировать в запечатанные типы, можно вручную объединить экземпляры SerializersModule из разных модулей.

// Module A
@Serializable data object RouteA1 : NavKey
@Serializable data object RouteA2 : NavKey

val serializerModuleA = SerializersModule {
    polymorphic(NavKey::class) {
        subclass(RouteA1::class, RouteA1.serializer())
        subclass(RouteA2::class, RouteA2.serializer())
    }
}

// Module B
@Serializable data class RouteB1(val id: String) : NavKey
@Serializable data class RouteB2(val id: String) : NavKey

val serializerModuleB = SerializersModule {
    polymorphic(NavKey::class) {
        subclass(RouteB1::class, RouteB1.serializer())
        subclass(RouteB2::class, RouteB2.serializer())
    }
}

// Module app
private val config = SavedStateConfiguration {
    serializersModule = serializerModuleA + serializerModuleB
}
val backStack = rememberNavBackStack(config, RouteA1)

Этот подход обеспечивает высокую гибкость и слабую связанность, однако требует большего объёма ручного сопровождения. Как и в подходе с несколькими модулями с объединёнными запечатанными типами, для динамического формирования списка сериализаторов можно использовать DI, что повысит гибкость.

Что дальше

Подробная информация о Navigation 3 доступна на портале Android Developers. Хотя в некоторых разделах документации используются специфичные для Android примеры, основные понятия и принципы навигации одинаковы для всех платформ:

  • Обзор Navigation 3 с рекомендациями по управлению состоянием, модульной организации кода навигации и анимации.

  • Переход с Navigation 2 на Navigation 3. Navigation 3 проще воспринимать как новую библиотеку, а не новую версию существующей, поэтому речь идёт скорее о переписывании, чем о переходе. Тем не менее в руководстве описаны основные шаги.

7 июля 2026 г.
Навигация в ComposeНавигация и маршрутизация

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/multiplatform/compose-navigation-3.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API