מעבר לניווט 3

‫Navigation 3 מייצג שינוי מהותי באופן שבו Jetpack Compose מטפל במצב הניווט, ומציע יתרונות ארכיטקטוניים משמעותיים בהשוואה ל-Navigation 2.

הסבר על השינויים בארכיטקטורה והשלבים הנדרשים להעברת אפליקציית Wear Compose מ-Navigation 2 ל-Navigation 3.

היתרונות העיקריים של Navigation 3

  • שליטה ישירה במערך האחורי: NavBackStack הוא בעצם רשימה משתנה של אובייקטים מסוג NavKey, שמייצגים את היסטוריית המסכים שהמשתמש ביקר בהם. אתם שולטים בה בדיוק כמו בכל רשימת Kotlin MutableList ‏ (add, ‏ removeLast, ‏ clear). אתם יכולים לשנות את הרשימה ישירות כדי לבצע פעולות ניווט, כמו הוספת מקש כדי להתקדם או הסרת מקש כדי לחזור אחורה.
  • עיצוב שמתחיל ב-Compose: מקבץ הפעילויות הקודמות (back stack) מעוצב כמצב סטנדרטי שניתן לצפייה. שינוי היסטוריית הניווטים מתנהג בדיוק כמו עדכון של כל מצב אחר של Compose, ומפעיל אוטומטית את ההרכבה מחדש כדי להציג את המסך הנוכחי.
  • בטוחים לשימוש כברירת מחדל: מסלולים מבוססי-מחרוזות מוסרים לחלוטין. הניווט מתבסס על אובייקטים של נתונים ועל מחלקות נתונים שניתן לסדר אותם בסדרות.
  • הפרדה בין רכיבי התצוגה (אסטרטגיות של סצנות): שכבת המעבר של ממשק המשתמש (NavDisplay ו-SwipeDismissableSceneStrategy) מופרדת לחלוטין ממעקב המצב (NavBackStack), וכך אפשר לשלב בצורה פשוטה יותר מעברים מובנים של ניווט ב-Wear OS.

שלבים בהעברה

1. עדכון יחסי תלות

מסירים את התלות הישנה androidx.wear.compose:compose-navigation ומוסיפים את התלויות החדשות של Navigation 3 המפוצל, יחד עם תמיכה בסריאליזציה של Kotlin.

הסרה:

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

הוספה:

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. עדכון היעדים להטמעה של NavKey

בניווט 2, יכול להיות שהשתמשתם במחרוזות או באובייקטים גנריים לניתוב. ב-Navigation 3, חובה להטמיע את ממשק התיוג NavKey ולהוסיף את ההערה @Serializable לכל אובייקט מסך.

מדוע זה נדרש? כדי שאפשר יהיה לשמור את מחסנית החזרה ולשחזר אותה אחרי השבתת התהליך, navigation3-runtime הבסיסי מסתמך על kotlinx-serialization כדי לבצע סריאליזציה של המצב.

לפני (ניווט 2 – מסלולים גנריים בטוחים לטיפוס):

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

אחרי (ניווט 3 – NavKey + Serializable):

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

    @Serializable
    data object List : MigrationScreen
}

3. החלפת לוגיקת הניתוב (NavController ל-NavBackStack)

מחליפים את NavController ב-NavBackStack שאותחל באמצעות rememberNavBackStack. בנוסף, צריך ליצור מופע של SwipeDismissableSceneStrategy במיוחד בשביל Wear OS.

לפני (ניווט 2):

val navController = rememberSwipeDismissableNavController()

אחרי (ניווט 3):

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

4. מחליפים את NavHost ב-NavDisplay וב-entryProvider DSL

הקונטיינר NavHost וה-DSL הפנימי שלו composable("route") { ... } builder מוחלפים ב-NavDisplay וב-entryProvider { entry<Key> { ... } } DSL.

לפני (ניווט 2):

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

אחרי (ניווט 3):

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