Migracja do Navigation 3

Nawigacja 3 to zasadnicza zmiana w sposobie, w jaki Jetpack Compose obsługuje stan nawigacji, i oferuje znaczące zalety architektoniczne w porównaniu z Nawigacją 2.

Poznaj zmiany w architekturze i kroki wymagane do migracji aplikacji Wear Compose z Nawigacji 2 do Nawigacji 3.

Główne zalety Nawigacji 3

  • Bezpośrednia kontrola stosu wstecznego: NavBackStack to zasadniczo tylko modyfikowalna lista obiektów NavKey reprezentujących historię ekranów odwiedzonych przez użytkownika. Możesz ją kontrolować tak samo jak każdą inną listę Kotlin MutableList (add, removeLast, clear). Możesz bezpośrednio manipulować listą, aby wykonywać działania związane z nawigacją, takie jak dodawanie klucza, aby przejść dalej lub usuwanie klucza, aby wrócić.
  • Projekt oparty na Compose: stos wsteczny jest modelowany jako standardowy stan obserwowalny. Modyfikowanie historii nawigacji działa dokładnie tak samo jak aktualizowanie dowolnego innego stanu Compose, automatycznie wywołując ponowne komponowanie w celu wyświetlenia bieżącego ekranu.
  • Domyślnie bezpieczne typy: trasy oparte na ciągach znaków są całkowicie wyeliminowane. Nawigacja wykorzystuje serializowalne obiekty danych i klasy danych.
  • Oddzielone prezentacje (strategie scen): warstwa przejścia interfejsu (NavDisplay i SwipeDismissableSceneStrategy) jest całkowicie oddzielona od śledzenia stanu (NavBackStack), co umożliwia prostszą integrację wbudowanych przejść nawigacji Wear OS.

Kroki migracji

1. Zaktualizuj zależności

Usuń starą zależność androidx.wear.compose:compose-navigation i dodaj nowe podzielone zależności Nawigacji 3 wraz z obsługą serializacji Kotlin.

Usuń:

implementation("androidx.wear.compose:compose-navigation:...")

Dodaj:

implementation("androidx.navigation3:navigation3-runtime:...") // State logic
implementation("androidx.navigation3:navigation3-ui:...")      // Display logic
implementation("androidx.wear.compose:compose-navigation3:...") // Wear gestures
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:...") // Requires compiler plugin

2. Zaktualizuj miejsca docelowe, aby zaimplementować NavKey

W Nawigacji 2 do routingu można było używać ciągów znaków lub obiektów ogólnych. W Nawigacji 3 musisz zaimplementować interfejs znacznika NavKey i oznaczyć każdy obiekt ekranu adnotacją @Serializable.

Dlaczego to jest wymagane? Aby można było zapisać i przywrócić stos wsteczny po śmierci procesu, bazowy navigation3-runtime używa kotlinx-serialization do serializacji stanu.

Przed (Nawigacja 2 – ogólne trasy bezpieczne pod względem typów):

sealed class Nav2Screen {
    data object Landing : Nav2Screen()
    data object List : Nav2Screen()
}

Po (Nawigacja 3 – NavKey + Serializable):

@Serializable
sealed interface MigrationScreen : NavKey {
    @Serializable
    data object Landing : MigrationScreen

    @Serializable
    data object List : MigrationScreen
}

3. Zastąp logikę routingu (NavController na NavBackStack)

Zastąp NavController elementem NavBackStack zainicjowanym za pomocą rememberNavBackStack. Musisz też utworzyć instancję the SwipeDismissableSceneStrategy specjalnie dla Wear OS.

Przed (Nawigacja 2):

val navController = rememberSwipeDismissableNavController()

Po (Nawigacja 3):

val backStack = rememberNavBackStack(MigrationScreen.Landing as NavKey)
val strategy = rememberSwipeDismissableSceneStrategy<NavKey>()

4. Zastąp NavHost elementem NavDisplay i DSL entryProvider

Kontener NavHost i jego wewnętrzny konstruktor composable("route") { ... } DSL są zastępowane przez NavDisplay i entryProvider { entry<Key> { ... } } DSL.

Przed (Nawigacja 2):

SwipeDismissableNavHost(navController = navController, startDestination = "menu") {
    composable("menu") {
        GreetingScreen(
            onShowList = { navController.navigate("list") }
        )
    }
    composable("list") {
        ListScreen()
    }
}

Po (Nawigacja 3):

NavDisplay(
    backStack = backStack,
    sceneStrategies = listOf(strategy),
    entryProvider = entryProvider {
        entry<MigrationScreen.Landing> {
            GreetingScreen(
                onShowList = { backStack.add(MigrationScreen.List) }
            )
        }
        entry<MigrationScreen.List> {
            ListScreen()
        }
    }
)