Composable页面间共享ViewModel失效:跨页面修改并观察mutableStateOf<Boolean>不生效
Ah, I see exactly what's going on here! The root issue is that Screen1 and Screen2 are creating separate instances of MyViewModel, so when you update the state in Screen2, it doesn't affect the instance Screen1 is observing.
Why this happens
By default, the hiltViewModel() function uses the ViewModelStoreOwner tied to the current navigation destination. Each screen in Jetpack Navigation gets its own dedicated ViewModelStore, which means your two screens end up with completely separate ViewModel instances. Modifying one has no impact on the other.
Fix 1: Share the ViewModel across your navigation graph
If both screens live under the same navigation graph, you can fetch the ViewModel tied to the graph's ViewModelStoreOwner instead of individual destinations. This guarantees both screens use the same instance:
Updated Screen1:
@Composable fun Screen1(navigate: () -> Unit) { val navController = LocalNavController.current // Fetch ViewModel linked to the entire navigation graph val vm: MyViewModel = hiltViewModel(navController.graph) val myTestBoolean = vm.testBoolean.value Column { // Added an else branch so you can see the initial false state clearly Text(text = "myTestBoolean is $myTestBoolean") Button(onClick = navigate) { Text(text = "Navigate to Screen2") } } }
Updated Screen2:
@Composable fun Screen2(navigate: () -> Unit) { val navController = LocalNavController.current val vm: MyViewModel = hiltViewModel(navController.graph) Button(onClick = { vm.setTestBoolean(true) navigate() }) { Text(text = "Back") } }
Fix 2: Tie the ViewModel to your Activity
If you want the ViewModel to be shared across your entire single-Activity app, you can use the Activity itself as the ViewModelStoreOwner:
Screen1:
@Composable fun Screen1(navigate: () -> Unit) { val activity = LocalActivity.current val vm: MyViewModel = hiltViewModel(activity) val myTestBoolean = vm.testBoolean.value Column { Text(text = "myTestBoolean is $myTestBoolean") Button(onClick = navigate) { Text(text = "Navigate to Screen2") } } }
Screen2:
@Composable fun Screen2(navigate: () -> Unit) { val activity = LocalActivity.current val vm: MyViewModel = hiltViewModel(activity) Button(onClick = { vm.setTestBoolean(true) navigate() }) { Text(text = "Back") } }
Bonus: Make your ViewModel more secure
Right now you're exposing testBoolean as a MutableState, which lets external code modify the state directly. For better encapsulation, switch to exposing an immutable State instead:
@HiltViewModel class MyViewModel @Inject constructor() : ViewModel() { private val _testBoolean = mutableStateOf(false) // Expose immutable State instead of MutableState val testBoolean: State<Boolean> = _testBoolean fun setTestBoolean(boolean: Boolean){ _testBoolean.value = boolean } }
This way, the only way to update the state is through the setTestBoolean method, keeping your state logic controlled and predictable.
内容的提问来源于stack exchange,提问作者HavanaSun

