مهارات Android
العرض على GitHubJetpack Navigation 3
android skills add navigation-3لنقل تطبيقك من Navigation 2 إلى Navigation 3، اتّبِع الخطوات التالية:
- أضِف اعتماديات Navigation 3.
- يُرجى تعديل مسارات التنقّل لتنفيذ واجهة
NavKey. - أنشئ فئات للاحتفاظ بحالة التنقّل وتعديلها.
- استبدِل
NavControllerبهذه الفئات. - انقل وجهاتك من
NavGraphفي "NavHost" إلىentryProvider. - استبدِل
NavHostبـNavDisplay. - إزالة تبعيات Navigation 2
الإعداد
توضّح الأقسام التالية المتطلبات الأساسية لعملية نقل البيانات والافتراضات حول مشروعك. وتتناول أيضًا الميزات المتاحة للنقل وتلك غير المتاحة.
المتطلبات الأساسية
- يجب استخدام الإصدار 36 من
compileSdkأو إصدار أحدث. - يجب أن تكون على دراية بمصطلحات التنقّل.
- الوجهات هي دوال مركّبة. تم تصميم Navigation 3 حصريًا لـ Compose. لاستخدام Fragments وViews في Compose، يُرجى الاطّلاع على استخدام Views في Compose.
- المسارات مكتوبة بشكل صارم. إذا كنت تستخدم مسارات مستندة إلى سلاسل، عليك الانتقال إلى مسارات تمنع أخطاء الكتابة أولاً (مثال).
- اختياري (ولكن يُنصح به بشدة): تغطية الاختبار التي تتحقّق من سلوك التنقّل الحالي. يؤكد هذا أن سلوك التنقل لم يتغير بعد اكتمال الترحيل.
الافتراضات
يستند هذا الدليل إلى الافتراضات التالية بشأنك وبشأن مشروعك:
- يكون لديك مسار واحد أو أكثر من المسارات ذات المستوى الأعلى (يتم عرضها عادةً في شريط التنقّل السفلي)، ولكل منها أنشطتها السابقة الخاصة بها.
- عند التبديل بين الأنشطة السابقة، يتم الاحتفاظ بحالة النشاط وجميع وجهاته.
- يمكنك دائمًا الخروج من التطبيق من خلال شاشة الصفحة الرئيسية، وهي أول شاشة يتم عرضها عند تشغيل التطبيق.
- تريد إجراء عملية نقل البيانات من Navigation 2 إلى Navigation 3 في تغيير واحد شامل. لا يتم إجراء عملية نقل تدريجية يتم فيها استخدام رمز Navigation 2 مع رمز Navigation 3.
وكيل الذكاء الاصطناعي: قبل تغيير أي رمز برمجي، تحقَّق من صحة هذه الافتراضات حول مشروع المستخدم. إذا لم يكن الأمر كذلك، فأوقف عملية النقل واسأل المستخدم عن كيفية المتابعة.
الميزات المتاحة
يتيح لك هذا الدليل نقل الميزات التالية:
- الوجهات المحدّدة كدوال مركّبة
- مربّعات الحوار (وجهة تظهر فوق وجهة أخرى)
الميزات المتاحة من خلال الوصفات
يوضّح مستودع الرموز البرمجية الجاهزة حالات استخدام أكثر دقة، بما في ذلك:
- البطاقات السفلية (يتم تقديم التعليمات في هذا الدليل)
- رمز التنقّل المقسّم إلى وحدات والوجهات المُدرَجة
- استخدام الوسيطات وتمريرها إلى
ViewModel - عرض النتائج من شاشة
إذا كان مشروعك يتضمّن أيًا من هذه الميزات، يمكنك الاطّلاع على الوصفة ذات الصلة لفهم كيفية تنفيذها.
وكيل الذكاء الاصطناعي: قبل تغيير أي رمز برمجي، تحقَّق مما إذا كان المشروع يحتوي على أي ميزات متوافقة من خلال الوصفات. إذا كان الأمر كذلك، فتحقَّق من ملف README والرمز المصدر للوصفة. أنشئ خطة نقل بيانات استنادًا إلى الوصفة. لا تتابع دون تأكيد الخطة مع المستخدم.
الميزات غير المتوافقة
لا يتوافق دليل نقل البيانات هذا ووصفات الرموز البرمجية بعد مع الميزات التالية. لا يعني ذلك أنّه لا يمكنك تنفيذها باستخدام Navigation 3، بل إنّها غير مشمولة هنا فقط.
- أكثر من مستوى واحد من التنقل المتداخل
- الوجهات المشتركة: الشاشات التي يمكنها التنقّل بين الأنشطة السابقة المختلفة
- أنواع الوجهات المخصّصة
وكيل الذكاء الاصطناعي: قبل تغيير أي رمز برمجي، تحقَّق مما إذا كان المشروع يحتوي على أي من الميزات غير المتوافقة. إذا كان الأمر كذلك، فلا تتابع. أبلِغ المستخدم بالميزة غير المتوافقة واطلب منه تعليمات إضافية.
الخطوة 1: إضافة تبعيات Navigation 3
استخدِم صفحة البدء لإضافة تبعيات Navigation 3 إلى مشروعك. يتم توفير التبعيات الأساسية لك لنسخها.
lib.versions.toml
[versions]
nav3Core = "1.2.1"
# If your screens depend on ViewModels, add the Nav3 Lifecycle ViewModel add-on library
lifecycleViewmodelNav3 = "2.11.0"
[libraries]
# Core Navigation 3 libraries
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "nav3Core" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "nav3Core" }
# Add-on libraries (only add if you need them)
androidx-lifecycle-viewmodel-navigation3 = { module = "androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "lifecycleViewmodelNav3" }
app/build.gradle.kts
dependencies {
implementation(libs.androidx.navigation3.ui)
implementation(libs.androidx.navigation3.runtime)
// If using the ViewModel add-on library
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
}
يُرجى أيضًا تعديل minSdk للمشروع إلى 23 وcompileSdk إلى 36. يمكنك العثور عليها عادةً
في app/build.gradle.kts أو lib.versions.toml.
الخطوة 2: تعديل مسارات التنقّل لتنفيذ واجهة NavKey
عدِّل كل مسار تنقّل لكي ينفّذ واجهة NavKey. يتيح لك ذلك استخدام rememberNavBackStack للمساعدة في حفظ حالة التنقّل.
قبل:
@Serializable data object RouteA
بعد:
@Serializable data object RouteA : NavKey
الخطوة 3: إنشاء فئات للاحتفاظ بحالة التنقّل وتعديلها
الخطوة 3.1: إنشاء عنصر احتفاظ بحالة التنقّل
انسخ الرمز التالي إلى ملف باسم NavigationState.kt. أضِف اسم الحزمة
لمطابقة بنية مشروعك.
// package com.example.project import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSerializable import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.toMutableStateList import androidx.navigation3.runtime.NavBackStack import androidx.navigation3.runtime.NavEntry import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.rememberDecoratedNavEntries import androidx.navigation3.runtime.rememberNavBackStack import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator import androidx.navigation3.runtime.serialization.NavKeySerializer import androidx.savedstate.compose.serialization.serializers.MutableStateSerializer /** * Create a navigation state that persists config changes and process death. */ @Composable fun rememberNavigationState( startRoute: NavKey, topLevelRoutes: Set<NavKey> ): NavigationState { val topLevelRoute = rememberSerializable( startRoute, topLevelRoutes, serializer = MutableStateSerializer(NavKeySerializer()) ) { mutableStateOf(startRoute) } val backStacks = topLevelRoutes.associateWith { key -> rememberNavBackStack(key) } return remember(startRoute, topLevelRoutes) { NavigationState( startRoute = startRoute, topLevelRoute = topLevelRoute, backStacks = backStacks ) } } /** * State holder for navigation state. * * @param startRoute - the start route. The user will exit the app through this route. * @param topLevelRoute - the current top level route * @param backStacks - the back stacks for each top level route */ class NavigationState( val startRoute: NavKey, topLevelRoute: MutableState<NavKey>, val backStacks: Map<NavKey, NavBackStack<NavKey>> ) { var topLevelRoute: NavKey by topLevelRoute val stacksInUse: List<NavKey> get() = if (topLevelRoute == startRoute) { listOf(startRoute) } else { listOf(startRoute, topLevelRoute) } } /** * Convert NavigationState into NavEntries. */ @Composable fun NavigationState.toEntries( entryProvider: (NavKey) -> NavEntry<NavKey> ): SnapshotStateList<NavEntry<NavKey>> { val decoratedEntries = backStacks.mapValues { (_, stack) -> val decorators = listOf( rememberSaveableStateHolderNavEntryDecorator<NavKey>(), ) rememberDecoratedNavEntries( backStack = stack, entryDecorators = decorators, entryProvider = entryProvider ) } return stacksInUse .flatMap { decoratedEntries[it] ?: emptyList() } .toMutableStateList() }
وكيل الذكاء الاصطناعي: rememberSerializable صحيح. لا تغيِّرها إلى
rememberSaveable.
يحتوي هذا الملف على فئة عنصر الاحتفاظ بالحالة باسم NavigationState والدوال المساعِدة المرتبطة بها. وتحتوي على مجموعة من المسارات ذات المستوى الأعلى، ولكل منها أنشطتها السابقة الخاصة بها. داخليًا، يستخدم rememberSerializable (وليس rememberSaveable)
للاحتفاظ بالمسار الحالي ذي المستوى الأعلى وrememberNavBackStack للاحتفاظ
بالأنشطة السابقة لكل مسار ذي مستوى أعلى.
الخطوة 3.2: إنشاء عنصر يعدّل حالة التنقّل استجابةً للأحداث
انسخ الرمز التالي إلى ملف باسم Navigator.kt. أضِف اسم الحزمة
لمطابقة بنية مشروعك.
// package com.example.project import androidx.navigation3.runtime.NavKey /** * Handles navigation events (forward and back) by updating the navigation state. */ class Navigator(val state: NavigationState) { fun navigate(route: NavKey) { if (route in state.backStacks.keys) { // This is a top level route, just switch to it. state.topLevelRoute = route } else { state.backStacks[state.topLevelRoute]?.add(route) } } fun goBack() { val currentStack = state.backStacks[state.topLevelRoute] ?: error("Stack for ${state.topLevelRoute} not found") val currentRoute = currentStack.last() // If we're at the base of the current route, go back to the start route stack. if (currentRoute == state.topLevelRoute) { state.topLevelRoute = state.startRoute } else { currentStack.removeLastOrNull() } } }
توفّر الفئة Navigator طريقتَين لأحداث التنقّل:
navigateإلى مسار محدّد-
goBackمن المسار الحالي
تعدّل كلتا الطريقتَين NavigationState.
الخطوة 3.3: إنشاء NavigationState وNavigator
أنشِئ مثيلات من NavigationState وNavigator بالنطاق نفسه
الذي تستخدمه NavController.
val navigationState = rememberNavigationState( // ... startRoute = <Insert your starting route>, topLevelRoutes = <Insert your set of top level routes> // ... ) val navigator = remember { Navigator(navigationState) }
الخطوة 4: استبدال "NavController"
استبدِل طرق أحداث التنقّل في NavController بما يعادلها في Navigator.
الحقل أو الطريقة |
ما يعادل |
|---|---|
|
|
|
|
|
|
استبدِل حقول NavController بحقول NavigationState.
الحقل أو الطريقة |
ما يعادل |
|---|---|
|
|
|
|
|
|
|
|
الحصول على المسار ذي المستوى الأعلى: يمكنك التنقّل للأعلى في التسلسل الهرمي من إدخال الأنشطة السابقة الحالي للعثور عليه. |
|
استخدِم NavigationState.topLevelRoute لتحديد العنصر المحدّد حاليًا في شريط التنقّل.
قبل:
// ... val isSelected = navController.currentBackStackEntryAsState().value?.destination.isRouteInHierarchy(key::class) // ... fun NavDestination?.isRouteInHierarchy(route: KClass<*>) = this?.hierarchy?.any { it.hasRoute(route) } ?: false
بعد:
val isSelected = key == navigationState.topLevelRoute
يُرجى التأكّد من إزالة جميع الإشارات إلى NavController، بما في ذلك
أي عمليات استيراد.
الخطوة 4.1: نقل المنطق المستند إلى مراحل النشاط
في Navigation 2، ينفّذ NavBackStackEntry واجهة LifecycleOwner، ما يتيح لك
الاستماع إلى الأحداث التي تتم في مراحل النشاط أو جمع التدفقات بطريقة تراعي مراحل النشاط باستخدام
navController.currentBackStackEntry.
في Navigation 3، يوفّر NavDisplay دالة LifecycleOwner على مستوى الإدخال
من خلال LocalLifecycleOwner.current لكل محتوى دالة مركّبة
للوجهة. لمزيد من المعلومات، يُرجى الاطّلاع على مراحل نشاط الوجهة.
يجب إجراء العمليات التي تراعي مراحل النشاط مباشرةً داخل المحتوى القابل للإنشاء في وجهتك من خلال الإشارة إلى LocalLifecycleOwner.current.
على سبيل المثال، إذا جمعت تدفقًا بطريقة تراعي مراحل النشاط باستخدام إدخال الأنشطة السابقة:
قبل:
// In your destination screen or host val lifecycleOwner = navController.currentBackStackEntry!! val state by flow.collectAsStateWithLifecycle(lifecycleOwner = lifecycleOwner)
بعد:
// Inside the destination composable val state by flow.collectAsStateWithLifecycle()
الخطوة 4.2: نقل نتائج الاختبارات الناجحة
في Navigation 2، كانت الوجهات تُعيد النتائج إلى الوجهات السابقة باستخدام
SavedStateHandle في NavBackStackEntry. بما أنّ SavedStateHandle يعتمد على حالة مثيل محفوظة، فإنّ البيانات التي تم عرضها تنجو من إيقاف العملية نهائيًا تلقائيًا.
قبل:
// Sender destination: navController.previousBackStackEntry?.savedStateHandle?.set("contact_key", contact) navController.popBackStack() // Receiver destination: val lifecycleOwner = LocalLifecycleOwner.current navController.currentBackStackEntry?.savedStateHandle ?.getLiveData<Contact>("contact_key") ?.observe(lifecycleOwner) { contact -> viewModel.onRecipientSelected(contact) }
في Navigation 3، أضِف
rememberResultEventBusNavEntryDecorator() إلى
NavDisplay.entryDecorators. في عملية ربط entryProvider
الخاصة بوجهة المُرسِل، يمكنك استرداد LocalResultEventBus.current
واستدعاء sendResult(). في الوجهة المستلِمة، استخدِم
ResultEffect لإعادة توجيه الحدث إلى ViewModel أو
تفعيل تأثير جانبي.
بعد:
// Sender destination (in entryProvider): entry<ContactPickerRoute> { val resultBus = LocalResultEventBus.current ContactPickerScreen( onContactSelected = { contact -> resultBus.sendResult<Contact>(result = contact) navigator.goBack() } ) } // Receiver destination: @Composable fun ComposeMessageScreen(viewModel: ComposeMessageViewModel = viewModel()) { ResultEffect<Contact> { contact -> viewModel.onRecipientSelected(contact) } ComposeMessageContent(recipient = viewModel.recipient) }
للمراقبة على مستوى الولاية، يمكنك الاتصال بالرقم resultBus.conflateAsState(). لمزيد من التفاصيل، يُرجى الاطّلاع على عرض النتائج.
الخطوة 5: نقل وجهاتك من NavGraph في "NavHost" إلى entryProvider
في Navigation 2، يمكنك تحديد وجهاتك
باستخدام NavGraphBuilder DSL،
عادةً داخل دالة lambda اللاحقة في NavHost. من الشائع استخدام دوال الإضافات هنا كما هو موضّح في تضمين رمز التنقّل.
في Navigation 3، يمكنك تحديد وجهاتك باستخدام entryProvider. يحلّ هذا الـ entryProvider مسارًا إلى NavEntry. الأهم من ذلك أنّ
entryProvider لا يحدّد علاقات العناصر الرئيسية بالعناصر الفرعية بين الإدخالات.
في دليل نقل البيانات هذا، يتم تصميم العلاقات بين النماذج الرئيسية والفرعية على النحو التالي:
- يحتوي
NavigationStateعلى مجموعة من المسارات ذات المستوى الأعلى (المسارات الرئيسية) ومجموعة لكل مسار. يتتبّع المسار الحالي ذي المستوى الأعلى ومجموعة المسارات المرتبطة به. - عند الانتقال إلى مسار جديد، يتحقّق
Navigatorمما إذا كان المسار هو مسار ذو مستوى أعلى. إذا كان الأمر كذلك، يتم تعديل المسار والمجموعة الحاليين على المستوى الأعلى. وإذا لم يكن كذلك، يكون مسارًا فرعيًا وتتم إضافته إلى الحزمة الحالية.
الخطوة 5.1: إنشاء entryProvider
أنشئ entryProvider باستخدام DSL في النطاق نفسه الذي يتضمّن
NavigationState.
val entryProvider = entryProvider<NavKey> { }
الخطوة 5.2: نقل الوجهات إلى entryProvider
لكل وجهة محدّدة داخل NavHost، اتّبِع الخطوات التالية استنادًا إلى نوع الوجهة:
navigation: حذفها مع المسار ليست هناك حاجة إلى "المسارات الأساسية" لأنّ المسارات ذات المستوى الأعلى يمكنها تحديد كل أنشطة سابقة متداخلة.composable<T>: انقل الملف إلىentryProviderوأعِد تسميته إلىentry، مع الاحتفاظ بمعلمة النوع. على سبيل المثال، يصبحcomposable<RouteA>entry<RouteA>.-
dialog<T>: اتّبِع الخطوات نفسها الموضّحة فيcomposable، ولكن أضِف البيانات الوصفية إلى الإدخال على النحو التالي:entry<T>(metadata = DialogSceneStrategy.dialog()). bottomSheet: يمكنك اتّباع وصفة البطاقة السفلية هنا. هذا مشابه لتعليماتdialog، باستثناء أنّBottomSheetSceneStrategyليس جزءًا من مكتبة Navigation 3 الأساسية، لذا يجب نسخه في مشروعك.
وكيل الذكاء الاصطناعي: عند حذف المسارات المستخدَمة لتحديد رسم بياني متداخل، استبدِل أي
إشارات إلى المسار المحذوف بالنوع المستخدَم لتحديد العنصر الفرعي الأول
في الرسم البياني المتداخل. على سبيل المثال، إذا كان الرمز الأصلي هو
navigation<BaseRouteA>{ composable<RouteA>{ ... } }، عليك حذف
BaseRouteA واستبدال أي إشارات إليه بـ RouteA. عادةً ما يجب إجراء هذا الاستبدال للقائمة المتاحة لشريط التنقل أو شريط التنقل الجانبي أو
درج التنقل.
يمكنك إعادة هيكلة دوال الإضافة NavGraphBuilder إلى دوال الإضافة EntryProviderScope<T>، ثم نقلها.
يمكنك الحصول على وسيطات التنقّل باستخدام المفتاح المقدَّم إلى دالة lambda اللاحقة في entry.
على سبيل المثال:
// ... import androidx.navigation.NavDestination import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.dialog import androidx.navigation.compose.navigation import androidx.navigation.compose.rememberNavController import androidx.navigation.navOptions import androidx.navigation.toRoute // ... @Serializable data object BaseRouteA @Serializable data class RouteA(val id: String) @Serializable data object BaseRouteB @Serializable data object RouteB @Serializable data object RouteD @Composable fun NavHostSnippet(navController: NavHostController) { NavHost(navController = navController, startDestination = BaseRouteA){ composable<RouteA>{ entry -> val id = entry.toRoute<RouteA>().id ScreenA(title = "Screen has ID: $id") } featureBSection() dialog<RouteD>{ ScreenD() } } } fun NavGraphBuilder.featureBSection() { navigation<BaseRouteB>(startDestination = RouteB) { composable<RouteB> { ScreenB() } } }
يصبح:
// ... import androidx.navigation3.runtime.EntryProviderScope import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.entryProvider import androidx.navigation3.scene.DialogSceneStrategy // ... @Serializable data class RouteA(val id: String) : NavKey @Serializable data object RouteB : NavKey @Serializable data object RouteD : NavKey val entryProvider = entryProvider { entry<RouteA>{ key -> ScreenA(title = "Screen has ID: ${key.id}") } featureBSection() entry<RouteD>(metadata = DialogSceneStrategy.dialog()){ ScreenD() } } fun EntryProviderScope<NavKey>.featureBSection() { entry<RouteB> { ScreenB() } }
الخطوة 6: استبدال NavHost بـ NavDisplay
استبدِل NavHost بـ NavDisplay.
- احذف "
NavHost" واستبدِله بـ "NavDisplay". - حدِّد
entries = navigationState.toEntries(entryProvider)كمَعلمة. يؤدي ذلك إلى تحويل حالة التنقّل إلى الإدخالات التي يعرضهاNavDisplayباستخدامentryProvider. - اربط "
NavDisplay.onBack" بـ "navigator.goBack()". يؤدي ذلك إلى تحديث حالة التنقّل فيnavigatorعند اكتمال معالِج الرجوع المضمّن فيNavDisplay. - إذا كانت لديك وجهات مربّعات حوار، أضِف
DialogSceneStrategyإلى مَعلمةsceneStrategiesفيNavDisplay.
على سبيل المثال:
NavDisplay( entries = navigationState.toEntries(entryProvider), onBack = { navigator.goBack() }, sceneStrategies = remember { listOf(DialogSceneStrategy()) } )
الخطوة 7: نقل الروابط العميقة
في Navigation 2، تم تحديد الروابط لصفحات معيّنة مباشرةً داخل الرسم البياني للتنقّل
باستخدام مَعلمة deepLinks للوجهات.
في Navigation 3، تتم إدارة الروابط العميقة بشكل مستقل عن واجهة مستخدم التنقّل. يمكنك تحديد DeepLinkMatcher ومطابقة الطلبات الواردة في نشاطك لإنشاء الأنشطة السابقة الأولية.
قبل:
في Navigation 2، ربما تكون قد حدّدت رابطًا لصفحة في التطبيق على النحو التالي:
composable<RouteA>( deepLinks = listOf( navDeepLink { uriPattern = "www.example.com/user/{id}" } ) ) { // ... }
بعد:
في Navigation 3، يمكنك تحديد UriDeepLinkMatcher للمسار:
val userMatcher = UriDeepLinkMatcher( DeepLinkUri("www.example.com/user/{id}"), serializer<RouteA>() )
بعد ذلك، في onCreate (وonNewIntent) الخاص بالنشاط، يمكنك مطابقة النية الواردة
وتهيئة الأنشطة السابقة:
val deepLinkMatchers: List<DeepLinkMatcher<*, *>> = listOf( userMatcher, ) class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val request = DeepLinkRequest(intent = intent) val matchResult = deepLinkMatchers .mapNotNull { it.match(request) } .maxOrNull() val backStack = when (matchResult) { null -> listOf(HomeKey) is BackStackMatchResult<*, *> -> { @Suppress("UNCHECKED_CAST") matchResult.backStack as List<NavKey> } else -> listOf(matchResult.key) } // Use backStack with NavDisplay } }
أنواع الوسيطات المخصّصة
في Navigation 2، تعاملت مع أنواع الوسيطات المخصّصة أو التابعة لجهات خارجية (مثل
LocalDateTime) باستخدام عمليات تنفيذ NavType مخصّصة وtypeMap.
في Navigation 3، حدِّد DeepLinkSerializer لإلغاء تسلسل الأنواع المخصّصة أو
التابعة لجهات خارجية من مَعلمات معرّف الموارد المنتظم (URI). لمزيد من التفاصيل، يُرجى الاطّلاع على التسلسل المخصّص باستخدام DeepLinkSerializer.
لمزيد من حالات الاستخدام المتقدّمة، بما في ذلك الأنشطة السابقة الاصطناعية وأدوات المطابقة المخصّصة، يُرجى الاطّلاع على دليل إتاحة الروابط لصفحات في التطبيق.
الخطوة 8: إزالة تبعيات Navigation 2
أزِل جميع عمليات استيراد Navigation 2 والتبعيات الخاصة بالمكتبة.
ملخّص
تهانينا! تم نقل مشروعك الآن إلى Navigation 3. إذا واجهت أنت أو وكيل الذكاء الاصطناعي أي مشاكل أثناء استخدام هذا الدليل، يمكنك الإبلاغ عن خطأ هنا.