You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Compose:如何在Dialog与Composable导航间共享ViewModel?

问题说明

当前代码可实现同一导航图下Composable之间的ViewModel共享,但Dialog与Composable无法复用同一个ViewModel实例,两者的ViewModel哈希值不一致。

原代码

navigation(startDestination = Stage.First.name, route = "main") {
    dialog(Stage.First.name){
        it.provideViewModel<MyViewModel> { vm ->
            MyDialog(vm)
        }
    }
    composable(Stage.Second.name) {
        it.provideViewModel<MyViewModel> { vm ->
            Second(vm)
        }
    }
    composable(Stage.Third.name) {
        it.provideViewModel<MyViewModel> { vm ->
            Third(vm)
        }
    }
}

@Composable
inline fun <reified T : ViewModel> NavBackStackEntry.provideViewModel(
    content: @Composable (T) -> Unit
) {
    val parentId = destination.parent!!.id
    val parentEntry = remember(this) { navCon.getBackStackEntry(parentId) }

    val viewModel = hiltViewModel<T>(parentEntry)
    "vmHashcode".log("${destination.route} : ${viewModel.hashCode()}")

    content(viewModel)
}

问题原因

原代码通过destination.parent!!.id关联ViewModel作用域,但Dialog的导航条目在父节点back stack entry的处理逻辑上与Composable存在差异,导致两者获取的父节点entry并非同一个,最终创建了独立的ViewModel实例。

解决方案

修改provideViewModel函数,直接通过导航图的固定route获取对应的back stack entry,确保Dialog和Composable绑定到同一个导航图层级的ViewModel作用域:

修改后的工具函数

@Composable
inline fun <reified T : ViewModel> NavBackStackEntry.provideViewModel(
    navGraphRoute: String,
    content: @Composable (T) -> Unit
) {
    val navController = navController
    val parentEntry = remember(this) { navController.getBackStackEntry(navGraphRoute) }

    val viewModel = hiltViewModel<T>(parentEntry)
    "vmHashcode".log("${destination.route} : ${viewModel.hashCode()}")

    content(viewModel)
}

修改后的导航调用

navigation(startDestination = Stage.First.name, route = "main") {
    dialog(Stage.First.name){
        // 传入当前导航图的route "main"
        it.provideViewModel<MyViewModel>("main") { vm ->
            MyDialog(vm)
        }
    }
    composable(Stage.Second.name) {
        it.provideViewModel<MyViewModel>("main") { vm ->
            Second(vm)
        }
    }
    composable(Stage.Third.name) {
        it.provideViewModel<MyViewModel>("main") { vm ->
            Third(vm)
        }
    }
}

原理说明

通过导航图的明确route获取对应的back stack entry,强制Dialog和Composable将ViewModel作用域绑定到同一个导航图层级,彻底避免了依赖destination.parent带来的不确定逻辑,确保ViewModel实例的共享一致性。

内容的提问来源于stack exchange,提问作者권오성

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 04:05:27