使用 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
扩展函数会创建目的地
其中显示了 ContactsScreen
可组合项。此外,它现在还可以连接
具有 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
确保屏幕和路由类型不会公开