Migracja do Navigation 3

Nawigacja 3 to zasadnicza zmiana w sposobie obsługi stanu nawigacji w Jetpack Compose, która oferuje znaczące zalety architektoniczne w porównaniu z Nawigacją 2.

Dowiedz się więcej o zmianach w architekturze i krokach wymaganych do migracji aplikacji Wear Compose z Nawigacji 2 do Nawigacji 3.

Główne zalety Nawigacji 3

  • Bezpośrednia kontrola nad stosem wstecznym: NavBackStack to zasadniczo lista modyfikowalna obiektów NavKey, która reprezentuje historię ekranów odwiedzonych przez użytkownika. Możesz ją kontrolować tak samo jak każdą inną listę w Kotlinie MutableList (add, removeLast, clear). Możesz bezpośrednio manipulować listą, aby wykonywać działania związane z nawigacją, np. dodawać klucz, aby przejść dalej, lub usuwać klucz, 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 rekompozycję w celu wyświetlenia bieżącego ekranu.
  • Domyślnie bezpieczne typy: trasy oparte na ciągach znaków są całkowicie wyeliminowane. Nawigacja wykorzystuje serializowane 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 w Wear OS.

Kroki migracji

1. Uaktualnij 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 w Kotlinie.

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 zagwarantować, że stos wsteczny można zapisać i przywrócić po śmierci procesu, bazowy navigation3-runtime używa kotlinx-serialization do serializacji stanu.

Przed (Nawigacja 2 – ogólne trasy bezpieczne dla 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()
        }
    }
)