使用 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
擴充功能函式構成無狀態畫面層級可組合函式和 Navigation 專屬邏輯之間的橋樑。此資料層也可定義該狀態的來源與事件處理方式。
範例
以下程式碼片段引入新的目的地,可顯示聯絡人的詳細資料,並更新現有的聯絡人清單目的地,以顯示導覽事件以顯示聯絡人的詳細資料。
以下是可以設為自身模組 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
可組合項。此外,它現在會將畫面與提供畫面 UI 狀態的 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
取得 UI 狀態,而不必從檢視畫面模型取得 UI 狀態。
// 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
確保畫面和路線類型不外洩