تضمين رمز التنقل

عند استخدام Kotlin DSL لإنشاء الرسم البياني، قد يكون من الصعب الحفاظ على الوجهات وأحداث التنقّل في ملف واحد. هذا صحيح خصوصًا إذا كانت لديك ميزات مستقلة متعددة.

استخراج الوجهات

يجب نقل وجهاتك إلى دوال الإضافة NavGraphBuilder. يجب أن يعيشوا بالقرب من المسارات التي تحددهم، والشاشات التي يعرضونها. على سبيل المثال، ضع في اعتبارك الرمز التالي على مستوى التطبيق الذي ينشئ وجهة تعرض قائمة جهات الاتصال:

// MyApp.kt

@Serializable
object Contacts

@Composable
fun MyApp() {
  ...
  NavHost(navController, startDestination = Contacts) {
    composable<Contacts> { ContactsScreen( /* ... */ ) }
  }
}

يجب نقل التعليمة البرمجية الخاصة بالتنقل إلى ملف منفصل:

// ContactsNavigation.kt

@Serializable
object Contacts

fun NavGraphBuilder.contactsDestination() {
    composable<Contacts> { ContactsScreen( /* ... */ ) }
}

// MyApp.kt

@Composable
fun MyApp() {
  ...
  NavHost(navController, startDestination = Contacts) {
     contactsDestination()
  }
}

أصبحت تعريفات المسارات والوجهات منفصلة الآن عن التطبيق الرئيسي ويمكنك تعديلها بشكلٍ مستقل. يعتمد التطبيق الرئيسي فقط على دالة إضافة واحدة. في هذه الحالة، يساوي NavGraphBuilder.contactsDestination().

تشكِّل دالة الإضافة NavGraphBuilder الجسر بين الدالة القابلة للإنشاء على مستوى الشاشة والمنطق الخاص بالتنقل. يمكن لهذه الطبقة أيضًا تحديد مصدر الحالة وكيفية التعامل مع الأحداث.

مثال

يقدّم المقتطف التالي وجهة جديدة لعرض تفاصيل جهة الاتصال، ويعدِّل وجهة قائمة جهات الاتصال الحالية لعرض حدث تنقّل لعرض تفاصيل جهة الاتصال.

إليك مجموعة نموذجية من الشاشات التي يمكن أن تكون internal في وحدتها الخاصة، لذلك لا يمكن للوحدات الأخرى الوصول إليها:

// ContactScreens.kt

// Displays a list of contacts
@Composable
internal fun ContactsScreen(
  uiState: ContactsUiState,
  onNavigateToContactDetails: (contactId: String) -> Unit
) { ... }

// Displays the details for an individual contact
@Composable
internal fun ContactDetailsScreen(contact: ContactDetails) { ... }

إنشاء وجهات

تنشئ دالة الإضافة NavGraphBuilder التالية وجهةً تعرِض ConversationScreen القابل للإنشاء. بالإضافة إلى ذلك، أصبح يربط الشاشة الآن بـ ViewModel الذي يوفر حالة واجهة مستخدم الشاشة ويعالج منطق العمل المرتبط بالشاشة.

يتم عرض أحداث التنقّل، مثل الانتقال إلى وجهة تفاصيل جهة الاتصال، على المتصل بدلاً من التعامل معها من خلال ViewModel.

// ContactsNavigation.kt

@Serializable
object Contacts

// Adds contacts destination to `this` NavGraphBuilder
fun NavGraphBuilder.contactsDestination(
  // Navigation events are exposed to the caller to be handled at a higher level
  onNavigateToContactDetails: (contactId: String) -> Unit
) {
  composable<Contacts> {
    // The ViewModel as a screen level state holder produces the screen
    // UI state and handles business logic for the ConversationScreen
    val viewModel: ContactsViewModel = hiltViewModel()
    val uiState = viewModel.uiState.collectAsStateWithLifecycle()
    ContactsScreen(
      uiState,
      onNavigateToContactDetails
    )
  }
}

يمكنك اتّباع الطريقة نفسها لإنشاء وجهة تعرض ContactDetailsScreen. في هذه الحالة، يمكنك الحصول على حالة واجهة المستخدم من نموذج العرض مباشرةً من NavBackStackEntry.

// ContactsNavigation.kt

@Serializable
internal data class ContactDetails(val id: String)

fun NavGraphBuilder.contactDetailsScreen() {
  composable<ContactDetails> { navBackStackEntry ->
    ContactDetailsScreen(contact = navBackStackEntry.toRoute())
  }
}

تضمين أحداث التنقّل

بالطريقة نفسها التي يتم بها تغليف الوجهات، يمكنك تغليف أحداث التنقّل لتجنُّب عرض أنواع المسارات بدون داعٍ. ويمكنك إجراء ذلك من خلال إنشاء دوال الإضافات على NavController.

// ContactsNavigation.kt

fun NavController.navigateToContactDetails(id: String) {
  navigate(route = ContactDetails(id = id))
}

تجميع كل ما تريده

تم فصل رمز التنقل لعرض جهات الاتصال بوضوح الآن عن الرسم البياني للتنقل في التطبيق. يحتاج التطبيق إلى ما يلي:

  • استدعاء وظائف الإضافات NavGraphBuilder لإنشاء وجهات
  • يمكنك ربط تلك الوجهات من خلال استدعاء وظائف الإضافة NavController لأحداث التنقل
// MyApp.kt

@Composable
fun MyApp() {
  ...
  NavHost(navController, startDestination = Contacts) {
     contactsDestination(onNavigateToContactDetails = { contactId ->
        navController.navigateToContactDetails(id = contactId)
     })
     contactDetailsDestination()
  }
}

الملخّص

  • قم بتغليف رمز التنقل الخاص بك لمجموعة ذات صلة من الشاشات عن طريق وضعها في ملف منفصل
  • عرض الوجهات من خلال إنشاء دوال الإضافات على NavGraphBuilder
  • عرض أحداث التنقّل من خلال إنشاء دوال الإضافات على NavController
  • يمكنك استخدام internal للحفاظ على خصوصية الشاشات وأنواع المسارات.