Halaman ini berisi praktik terbaik yang menyediakan keamanan jenis runtime untuk Navigation Kotlin DSL dan Navigation Compose. Singkatnya, Anda harus memetakan setiap layar aplikasi atau grafik navigasi ke file Navigasi basis per modul. Setiap file yang dihasilkan harus berisi semua informasi terkait Navigasi untuk tujuan yang telah ditentukan. File navigasi ini juga merupakan tempat pengubah visibilitas Kotlin memberikan keamanan jenis runtime:
- Fungsi keamanan jenis seluruh codelab dapat dilihat secara publik.
- Konsep khusus navigasi untuk layar atau grafik navigasi tertentu ditempatkan bersama dan disimpan secara pribadi dalam file yang sama agar tidak dapat diakses oleh bagian codebase lainnya.
Memisahkan grafik navigasi
Anda harus memisahkan grafik navigasi berdasarkan layar. Cara ini pada dasarnya merupakan pendekatan
yang sama seperti saat Anda membagi layar ke fungsi composable lain. Setiap
layar harus memiliki fungsi ekstensi NavGraphBuilder
.
Fungsi ekstensi ini adalah jembatan antara fungsi composable tingkat layar stateless dan logika khusus Navigasi. Lapisan ini juga dapat menentukan lokasi asal status dan cara penanganan peristiwa.
Berikut adalah ConversationScreen
standar yang dapat berupa internal
untuk modulnya sendiri
sehingga modul lain tidak dapat mengaksesnya:
// ConversationScreen.kt
@Composable
internal fun ConversationScreen(
uiState: ConversationUiState,
onPinConversation: () -> Unit,
onNavigateToParticipantList: (conversationId: String) -> Unit
) { ... }
Fungsi ekstensi NavGraphBuilder
berikut menambahkan composable
ConversationScreen
sebagai tujuan dari NavGraph
tersebut. Fitur ini
juga menghubungkan layar dengan ViewModel yang menyediakan status UI layar dan
menangani logika bisnis terkait layar. Peristiwa navigasi yang tidak dapat
ditangani oleh ViewModel ditampilkan kepada pemanggil.
// ConversationNavigation.kt
private const val conversationIdArg = "conversationId"
// Adds conversation screen to `this` NavGraphBuilder
fun NavGraphBuilder.conversationScreen(
// Navigation events are exposed to the caller to be handled at a higher level
onNavigateToParticipantList: (conversationId: String) -> Unit
) {
composable("conversation/{$conversationIdArg}") {
// The ViewModel as a screen level state holder produces the screen
// UI state and handles business logic for the ConversationScreen
val viewModel: ConversationViewModel = hiltViewModel()
val uiState = viewModel.uiState.collectAsStateWithLifecycle()
ConversationScreen(
uiState,
::viewModel.pinConversation,
onNavigateToParticipantList
)
}
}
File ConversationNavigation.kt
memisahkan kode dari library Navigasi
dari tujuan itu sendiri. Library ini juga memberikan enkapsulasi seputar konsep Navigasi
seperti rute atau ID argumen yang dirahasiakan karena
tidak boleh sampai bocor ke luar file ini. Peristiwa navigasi yang tidak dapat ditangani
di lapisan ini harus diinformasikan kepada pemanggil agar ditangani di level
yang tepat. Anda akan menemukan contoh peristiwa seperti ini dengan
onNavigateToParticipantList
dalam cuplikan kode di atas.
Navigasi keamanan jenis
Navigation Kotlin DSL yang di-build pada Navigation Compose saat ini tidak menawarkan keamanan jenis waktu kompilasi dari jenis yang disediakan oleh Safe Args ke grafik navigasi yang dibuat di file resource XML navigasi. Safe Args menghasilkan kode yang berisi class dan metode keamanan jenis untuk tujuan dan tindakan Navigasi. Namun, Anda dapat membuat struktur kode Navigasi agar aman dari error jenis saat runtime. Dengan cara ini, Anda dapat menghindari error dan memastikan bahwa:
- Argumen yang Anda berikan saat membuka grafik navigasi atau tujuan merupakan jenis yang tepat dan semua argumen yang diperlukan ada.
- Argumen yang Anda ambil dari
SavedStateHandle
adalah jenis argumen yang tepat.
Membuka tujuan
Setiap tujuan juga harus mengekspos fungsi ekstensi NavController
agar
tujuan lain dapat membuka tujuan tersebut dengan aman.
// ConversationNavigation.kt
fun NavController.navigateToConversation(conversationId: String) {
this.navigate("conversation/$conversationId")
}
Jika Anda ingin membuka layar aplikasi dengan
NavOptions
lain, seperti popUpTo, savedState, restoreState
, atau singleTop
,
saat membuka, teruskan parameter opsional ke fungsi ekstensi NavController
.
// HomeNavigation.kt
const val HomeRoute = "home"
fun NavController.navigateToHome(navOptions: NavOptions? = null) {
this.navigate(HomeRoute, navOptions)
}
Wrapper argumen keamanan jenis
Sebagai pilihan, Anda dapat membuat wrapper keamanan jenis untuk mengekstrak argumen dari
SavedStateHandle
untuk ViewModel Anda dan dari NavBackStackEntry
di konten tujuan untuk mendapatkan manfaat yang disebutkan
dalam pengantar bagian ini.
// ConversationNavigation.kt
private const val conversationIdArg = "conversationId"
internal class ConversationArgs(val conversationId: String) {
constructor(savedStateHandle: SavedStateHandle) :
this(checkNotNull(savedStateHandle[conversationIdArg]) as String)
}
// ConversationViewModel.kt
internal class ConversationViewModel(...,
savedStateHandle: SavedStateHandle
) : ViewModel() {
private val conversationArgs = ConversationArgs(savedStateHandle)
}
Menyusun grafik navigasi
Grafik navigasi menggunakan fungsi ekstensi keamanan jenis yang dijelaskan di atas untuk menambahkan tujuan dan membukanya.
Dalam contoh berikut, tujuan percakapan bersama dengan dua
tujuan lainnya, rumah dan daftar peserta, disertakan dalam tingkat aplikasi
NavHost
sebagai berikut:
// MyApp.kt
@Composable
fun MyApp(modifier: Modifier = Modifier) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = HomeRoute,
modifier = modifier
) {
homeScreen(
onNavigateToConversation = { conversationId ->
navController.navigateToConversation(conversationId)
}
)
conversationScreen(
onNavigateToParticipantList = { conversationId ->
navController.navigateToParticipantList(conversationId)
}
}
participantListScreen()
}
Keamanan jenis di grafik navigasi bertingkat
Anda harus memilih visibilitas yang tepat untuk modul yang menyediakan beberapa layar. Konsep ini sama dengan konsep untuk setiap metode di bagian di atas. Namun, mungkin akan terasa ganjil jika layar individual diekspos ke modul lain. Dalam hal ini, Anda harus memperlakukannya sebagai bagian dari alur mandiri yang lebih besar.
Kumpulan layar mandiri ini disebut grafik navigasi
bertingkat. Dengan begitu, Anda dapat menyertakan beberapa layar ke dalam satu
metode ekstensi NavGraphBuilder
. Metode ini menggunakan metode ekstensi NavController
tersebut secara bergantian untuk menautkan layar dalam modul yang sama secara bersamaan.
Pada contoh berikut, tujuan percakapan yang telah dijelaskan di bagian sebelumnya muncul dalam grafik navigasi bertingkat bersama dua tujuan lainnya, yaitu daftar percakapan dan daftar peserta.
// ConversationGraphNavigation.kt
private val ConversationGraphRoutePattern = "conversation"
fun NavController.navigateToConversationGraph(navOptions: NavOptions? = null) {
this.navigate(ConversationGraphRoutePattern, navOptions)
}
fun NavGraphBuilder.conversationGraph(navController: NavController) {
navigation(
startDestination = ConversationListRoutePattern,
route = ConversationGraphRoutePattern
) {
conversationListScreen(
onNavigateToConversation = { conversationId ->
navController.navigateToConversation(conversationId)
}
)
conversationScreen(
onNavigateToParticipantList = { conversationId ->
navController.navigateToParticipantList(conversationId)
}
)
partipantList()
}
Anda dapat menggunakan beberapa grafik navigasi bertingkat di NavHost
tingkat aplikasi sebagai
berikut:
// MyApp.kt
@Composable
fun MyApp(modifier: Modifier = Modifier) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = HomeGraphRoutePattern
modifier = modifier
) {
homeGraph(
navController,
onNavigateToConversation = {
navController.navigateToConversationGraph()
}
}
conversationGraph(navController)
}