التنقّل 3

‫Navigation 3 هي مكتبة تنقّل مصمّمة من البداية لتطبيق Jetpack Compose. يوضّح هذا الدليل كيفية استخدام Navigation 3 في تطبيقات Wear OS.

المفاهيم الأساسية

  • NavKey: معرّف قابل للتسلسل وآمن من حيث النوع لوجهة (شاشة) في تطبيقك.
  • NavBackStack: قائمة قابلة للتعديل من مثيلات NavKey التي تمثّل سجلّ التنقّل. يمكنك إضافة العناصر وإزالتها مباشرةً من هذه القائمة.
  • rememberNavBackStack: دالة مركّبة تنشئ الأنشطة السابقة وتحفظها عند حدوث تغييرات في الإعدادات أو إيقاف العملية نهائيًا.
  • NavDisplay: مكوّن واجهة المستخدم الأساسي الذي يتتبّع سجلّ التتبّع ويعرض الشاشة النشطة.
  • EntryProvider: لغة نطاق خاصة بمخطط الربط تربط NavKey بواجهة المستخدم @Composable الفعلية.
  • SwipeDismissableSceneStrategy: استراتيجية خاصة بأجهزة Wear تغلف شاشاتك بإيماءة السحب للإغلاق وتعالج الرسوم المتحركة المضمّنة للرجوع.

الخطوة 1: إضافة التبعيات

أضِف تبعيات Navigation 3 وWear Compose وSerialization المطلوبة إلى مشروعك.

أنيق

dependencies {
    // Core Navigation 3 APIs
    implementation "androidx.navigation3:navigation3-runtime:1.2.0-alpha02"
    implementation "androidx.navigation3:navigation3-ui:1.2.0-alpha02"

    // Wear OS specific Navigation 3 integration
    implementation "androidx.wear.compose:compose-navigation3:1.6.1"

    // Kotlinx Serialization for type-safe routing
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0"
}

Kotlin

dependencies {
    // Core Navigation 3 APIs
    implementation("androidx.navigation3:navigation3-runtime:1.2.0-alpha02")
    implementation("androidx.navigation3:navigation3-ui:1.2.0-alpha02")

    // Wear OS specific Navigation 3 integration
    implementation("androidx.wear.compose:compose-navigation3:1.6.1")

    // Kotlinx Serialization for type-safe routing
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0")
}

الخطوة 2: تحديد الوجهات (NavKey)

يتم تعريف الشاشات على أنّها كائنات أو فئات بيانات قابلة للتسلسل ومحدّدة النوع تنفّذ واجهة NavKey.

@Serializable
sealed interface Screen : NavKey {
    @Serializable
    data object Home : Screen

    @Serializable
    data class Details(val itemId: String) : Screen
}

الخطوة 3: إعداد NavDisplay وسجلّ التتبّع

في جذر تطبيقك، عليك إعداد الأنشطة السابقة واستراتيجية مشهد Wear OS، ثم ربطهما بـ NavDisplay.

// 1. Create the persistent back stack starting at the Home screen
val backStack = rememberNavBackStack(Screen.Home)

// 2. Initialize the Wear OS swipe-to-dismiss strategy
val strategy = rememberSwipeDismissableSceneStrategy<NavKey>()

// 3. Render the NavDisplay
NavDisplay(
    backStack = backStack,
    sceneStrategies = listOf(strategy),
    entryProvider = entryProvider {
        // 4. Map keys to Composables
        entry<Screen.Home> {
            HomeScreen(
                onNavigateToDetails = { id -> backStack.add(Screen.Details(id)) }
            )
        }
        entry<Screen.Details> { key ->
            DetailsScreen(
                itemId = key.itemId,
                onBack = { backStack.removeAt(backStack.lastIndex) }
            )
        }
    }
)

الخطوة 4: تنفيذ إجراءات التنقّل

بما أنّ سجلّ الأنشطة السابقة هو مجرد MutableList مخصّصة، فإنّ التنقّل بسيط للغاية. يمكنك تنفيذ العمليات مباشرةً على مثيل backStack:

  • التنقّل للأمام: backStack.add(Screen.Details("123"))
  • التنقّل للخلف: backStack.removeLast() أو backStack.removeLastOrNull()
  • محو سجلّ التتبّع وإعادة ضبطه: backStack.clear(); backStack.add(Screen.Home) (أو استخدِم عمليات القائمة لاستبدال سجلّ التتبّع).

الخطوة 5: (اختيارية) تحديد نطاق `ViewModel`s للوجهات

يكون نطاق ViewModels تلقائيًا هو Activity. توفر Navigation 3 عنصرًا معينًا (lifecycle-viewmodel-navigation3) لتحديد نطاق ViewModel بأمان لـ NavEntry في الأنشطة السابقة. عند إزالة الوجهة من سجلّ التتبّع، تتم إزالة `ViewModel`.

  1. أضِف التبعية:

    implementation("androidx.lifecycle:lifecycle-viewmodel-navigation3:...")
    
  2. أضِف أداة تزيين متجر `ViewModel` إلى NavDisplay's entryDecorators. عليك أيضًا تضمين SaveableStateHolderNavEntryDecorator بشكلٍ صريح عند توفير أدوات تزيين مخصّصة للاحتفاظ بحالة Compose rememberSaveable:

    NavDisplay(
        backStack = backStack,
        sceneStrategies = listOf(strategy),
        entryDecorators = listOf(
            rememberSaveableStateHolderNavEntryDecorator(),
            rememberViewModelStoreNavEntryDecorator()
        ),
        entryProvider = entryProvider {
            entry<Screen.Home> {
                // Any viewModel() requested here will be scoped to this NavEntry
                val viewModel: HomeViewModel = viewModel()
            }
        }
    )