路由配置
app/src/main/java/com/joker/kit/navigation/routes 负责声明所有页面/弹窗的“类型安全路由”。每个路由都是可序列化的数据类(或 data object),Compose Navigation 可以直接通过 composable<Route>() 推断路径与参数。Feature 模块在 feature/*/navigation 中注册路由,最后由 AppNavHost 汇总。
路由定义约定
- 文件划分:按业务域拆分,例如
MainRoutes、AuthRoutes、UserRoutes。需要新增商品模块时,可创建GoodsRoutes。 - 注解:统一使用
@Serializable,确保 Compose Navigation 在编译期生成路径解析器。 - 参数:尽量使用显式字段(
goodsId: Long、tab: String等),避免字符串拼接。例如:
kotlin
object UserRoutes {
@Serializable
data object Info
}即便是无参页面也声明为 data object,保证写法一致。
在 NavGraph 中注册
Feature 模块通过 NavGraphBuilder 扩展函数注册页面。示例如 feature/user/navigation/UserNavigation.kt:
kotlin
@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.userInfoScreen(sharedTransitionScope: SharedTransitionScope) {
composable<UserRoutes.Info> {
UserInfoRoute()
}
}Compose 1.7+ 支持
composable<Route>()写法,无需再手写route = "xxx"字符串,也不需要navArgument。
各模块再提供一个聚合函数(如 mainGraph, userGraph),用于在 AppNavHost 中一次性挂载。
路由参数读取
Compose Navigation 已提供 toRoute<T>() 扩展,能够自动解析 @Serializable 路由对象。常见用法:
- 在 Composable 中读取kotlin
composable<GoodsRoutes.Detail> { backStackEntry -> val route = backStackEntry.toRoute<GoodsRoutes.Detail>() GoodsDetailRoute(goodsId = route.goodsId) } - 在 ViewModel 中读取
基类BaseNetWorkViewModel会把SavedStateHandle注入子类,只需:kotlin@HiltViewModel class AddressDetailViewModel @Inject constructor( navigator: AppNavigator, userState: UserState, savedStateHandle: SavedStateHandle ) : BaseNetWorkViewModel<AddressDetail>(navigator, userState, savedStateHandle) { private val route = savedStateHandle.toRoute<UserRoutes.AddressDetail>() private val addressId = route.addressId // ... }
toRoute<T>() 会将字符串路由反序列化为 T,无需手写 SavedStateHandle["id"]。
扩展示例(以“商品”模块为例)
假设现在需要新增一个商品模块,可参考以下流程:
- 定义路由:在
navigation/routes/GoodsRoutes.kt中集中声明kotlinobject GoodsRoutes { @Serializable data class Detail(val goodsId: Long) @Serializable data object Search @Serializable data object Comment } - 声明页面:在
feature/goods/navigation/GoodsDetailNavigation.kt等文件中注册kotlin搜索、评论等页面也各自提供fun NavGraphBuilder.goodsDetailScreen() { composable<GoodsRoutes.Detail> { GoodsDetailRoute() } }NavGraphBuilder扩展。 - 组合 Graph:创建
GoodsGraph.kt,统一调用goodsDetailScreen()、goodsSearchScreen()等,并暴露fun NavGraphBuilder.goodsGraph(...)。一个简化示例如下:kotlin这层只负责拼装当前功能模块的所有页面,并提供给fun NavGraphBuilder.goodsGraph(navController: NavHostController) { goodsDetailScreen() goodsSearchScreen() goodsCommentScreen() goodsCategoryScreen() }AppNavHost。 - 挂载到 AppNavHost:在
AppNavHost的NavHost { ... }中新增一行kotlin这样 AppNavHost 只需按模块注册 Graph,像现有的goodsGraph(navController, this@SharedTransitionLayout)mainGraph、authGraph、userGraph一样,大幅减少多人协作时对AppNavHost的频繁改动。
通过这种方式,每个业务模块都拥有独立的路由文件与导航函数,避免导航逻辑散落各处。
建议
- 新建路由时务必同时更新:
routes/*、feature/*/navigation、AppNavHost。三者缺一不可。 - 若页面需要从
SavedStateHandle读取参数,可使用 Compose Navigation 提供的backStackEntry.toRoute<Route>()扩展(Compose 1.7+ 自带),无需自己解析字符串。 - Graph 函数只负责注册
composable,不要在里面直接处理导航跳转或状态逻辑,保持纯粹可测试。 - 若已有存量模块,可先比照其
Routes+Navigation+Graph结构,再按上述步骤迁移,保证层级一致。