מעבר לניווט 3

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

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

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

  • שליטה ישירה במערך ההיסטוריה של החזרה: NavBackStack הוא בעצם רשימה משתנה של אובייקטים מסוג NavKey, שמייצגת את היסטוריית המסכים שהמשתמש ביקר בהם. אתם שולטים בה בדיוק כמו בכל MutableList (add, ‏ removeLast, ‏ clear) של Kotlin. אתם יכולים לשנות את הרשימה ישירות כדי לבצע פעולות ניווט, כמו הוספת מקש כדי להתקדם או הסרת מקש כדי לחזור אחורה.
  • עיצוב שמתחיל בכתיבת הודעה: מקבץ פעילויות קודמות (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.

מדוע זה נדרש? כדי להבטיח שאפשר לשמור את מקבץ הפעילויות הקודמות (back stack) ולשחזר אותה אחרי השבתת תהליך, 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") { ... } שלו מוחלפים ב-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()
        }
    }
)