Mengenkapsulasi kode navigasi

Saat menggunakan DSL Kotlin untuk membuat grafik, menyimpan tujuan dan peristiwa navigasi dalam satu file mungkin sulit dikelola. Hal ini terutama berlaku jika Anda memiliki beberapa fitur independen.

Mengekstrak tujuan

Anda harus memindahkan tujuan ke fungsi ekstensi NavGraphBuilder. Tombol tersebut harus berada di dekat rute yang menentukannya dan layar yang ditampilkan. Misalnya, perhatikan kode tingkat aplikasi berikut yang membuat tujuan yang menampilkan daftar kontak:

// MyApp.kt

@Serializable
object Contacts

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

Anda harus memindahkan kode khusus navigasi ke file terpisah:

// ContactsNavigation.kt

@Serializable
object Contacts

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

// MyApp.kt

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

Definisi rute dan tujuan kini terpisah dari aplikasi utama dan Anda dapat memperbaruinya secara terpisah. Aplikasi utama hanya bergantung pada satu fungsi ekstensi. Dalam hal ini, nilainya adalah NavGraphBuilder.contactsDestination().

Fungsi ekstensi NavGraphBuilder membentuk penghubung antara fungsi composable tingkat layar stateless dan logika khusus Navigasi. Lapisan ini juga dapat menentukan asal status dan cara Anda menangani peristiwa.

Contoh

Cuplikan berikut memperkenalkan tujuan baru untuk menampilkan detail kontak, dan memperbarui tujuan daftar kontak yang ada untuk mengekspos peristiwa navigasi guna menampilkan detail kontak.

Berikut adalah kumpulan layar standar yang dapat berupa internal untuk modulnya sendiri sehingga modul lain tidak dapat mengaksesnya:

// 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) { ... }

Membuat tujuan

Fungsi ekstensi NavGraphBuilder berikut membuat tujuan yang menampilkan composable ConversationScreen. Selain itu, kode ini sekarang menghubungkan layar dengan ViewModel yang menyediakan status UI layar dan menangani logika bisnis terkait layar.

Peristiwa navigasi, seperti menavigasi ke tujuan detail kontak, ditampilkan kepada pemanggil, bukan ditangani oleh 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
    )
  }
}

Anda dapat menggunakan pendekatan yang sama untuk membuat tujuan yang menampilkan ContactDetailsScreen. Dalam hal ini, alih-alih mendapatkan status UI dari model tampilan, Anda dapat memperolehnya langsung dari NavBackStackEntry.

// ContactsNavigation.kt

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

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

Mengenkapsulasi peristiwa navigasi

Dengan cara yang sama seperti mengenkapsulasi tujuan, Anda dapat mengenkapsulasi peristiwa navigasi untuk menghindari mengekspos jenis rute yang tidak perlu. Lakukan hal ini dengan membuat fungsi ekstensi di NavController.

// ContactsNavigation.kt

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

Satukan

Kode navigasi untuk menampilkan kontak kini terpisah dengan rapi dari grafik navigasi aplikasi. Aplikasi harus:

  • Memanggil fungsi ekstensi NavGraphBuilder untuk membuat tujuan
  • Hubungkan tujuan tersebut dengan memanggil fungsi ekstensi NavController untuk peristiwa navigasi
// MyApp.kt

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

Ringkasan

  • Mengenkapsulasi kode navigasi untuk serangkaian layar terkait dengan menempatkannya dalam file terpisah
  • Mengekspos tujuan dengan membuat fungsi ekstensi di NavGraphBuilder
  • Mengekspos peristiwa navigasi dengan membuat fungsi ekstensi di NavController
  • Menggunakan internal untuk menjaga kerahasiaan layar dan jenis rute