如何在Compose独立导航图中创建Hilt共享ViewModel?
基于Hilt在嵌套导航图间共享ViewModel的方案
核心思路
既然两个页面同属于ProductGraph这个嵌套导航图,最合理的方案是将ViewModel的作用域绑定到该导航图的BackStackEntry上——这样ViewModel的生命周期与整个导航图绑定(只有当用户完全离开ProductGraph时才会销毁),既符合业务边界,又避免在全局或Activity层级创建实例的冗余问题。
具体实现步骤
- 确保
ProductGraph的路由是固定唯一的,用于定位对应的导航栈条目 - 在导航图内部获取该导航栈的
BackStackEntry实例 - 通过Hilt的
hiltViewModel()方法,传入该BackStackEntry作为ViewModelStoreOwner,实现同导航图内的Composable共享同一ViewModel实例
代码示例
1. 定义共享ViewModel
@HiltViewModel class SharedProductViewModel @Inject constructor( // 注入所需依赖,比如产品仓库等 ) : ViewModel() { // 用于存储选中的产品,供详情页读取 val selectedProduct = mutableStateOf<Product?>(null) fun selectProduct(product: Product) { selectedProduct.value = product } }
2. 修改ProductGraph导航代码
fun NavGraphBuilder.productGraph( navController: NavController ) { navigation( startDestination = ProductRoutes.ProductListScreen.route, route = ProductRoutes.ProductGraph.route ) { // 缓存ProductGraph对应的BackStackEntry,避免重复获取 val productGraphBackStackEntry = remember { navController.getBackStackEntry(ProductRoutes.ProductGraph.route) } composable(route = ProductRoutes.ProductListScreen.route) { val viewModel: SharedProductViewModel = hiltViewModel(productGraphBackStackEntry) ProductListScreen(navController = navController, viewModel = viewModel) } composable(route = ProductRoutes.ProductDetailScreen.route) { val viewModel: SharedProductViewModel = hiltViewModel(productGraphBackStackEntry) ProductDetailScreen(navController = navController, viewModel = viewModel) } } }
3. 页面中使用ViewModel
以ProductListScreen为例,点击产品时存储选中项:
@Composable fun ProductListScreen(navController: NavController, viewModel: SharedProductViewModel) { // 假设这里有产品列表UI LazyColumn { items(products) { product -> ProductItem(product) { viewModel.selectProduct(product) navController.navigate(ProductRoutes.ProductDetailScreen.route) } } } }
ProductDetailScreen中读取选中的产品:
@Composable fun ProductDetailScreen(navController: NavController, viewModel: SharedProductViewModel) { viewModel.selectedProduct.value?.let { product -> // 展示产品详情UI ProductDetailContent(product) } ?: run { // 处理未选中产品的情况,比如返回列表页 LaunchedEffect(Unit) { navController.popBackStack() } } }
注意事项
- 必须保证
ProductRoutes.ProductGraph.route是固定不变的,否则无法正确获取对应的BackStackEntry - 必须使用
hiltViewModel()而非普通的viewModel(),才能让Hilt正确注入依赖 - 该方案的ViewModel生命周期与
ProductGraph完全绑定,当用户从ProductGraph跳转到其他导航图时,ViewModel会被销毁,符合内存优化原则
内容的提问来源于stack exchange,提问作者JustSightseeing
相关产品推荐
相关产品推荐

