更新ViewModel的StateFlow后UI未重组问题排查
问题原因与解决方案
核心问题
你在StartScreen的按钮点击事件中,每次都新建了一个GViewModel实例,而非使用和BigBrother共享的同一个ViewModel对象:
BigBrother里的gViewModel: GViewModel = viewModel()是通过Jetpack Compose的viewModel()函数获取的,它会在当前页面(ViewModelStoreOwner)的范围内复用同一个实例。- 但你写的
GViewModel().redirection(...)会每次点击都创建全新的ViewModel,你更新的是这个新实例的内部状态,而主Composable监听的是最初那个ViewModel的uiState,两者完全无关,所以主页面的gUiState不会有任何变化。
修复方案
把主Composable的ViewModel实例传递给StartScreen,让它操作同一个对象:
- 修改
StartScreen的定义,添加ViewModel参数:
@Composable fun StartScreen( gViewModel: GViewModel, modifier: Modifier = Modifier ) { Log.d(TAG, "Launching START") Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, ) { Button(onClick = { gViewModel.redirection("CHOOSER") Log.d(TAG, "Button1 clicked, launching CHOOSER") }) { Text("New") } Button(onClick = { gViewModel.redirection("GALLERY") Log.d(TAG, "Button2 clicked, launching GALLERY") }) { Text("Browse") } } }
- 在
BigBrother中调用StartScreen时传入ViewModel:
@Composable fun BigBrother(gViewModel: GViewModel = viewModel()) { val gUiState by gViewModel.uiState.collectAsState() Log.d(TAG, "Chosen path is ${gUiState.direction}") when (gUiState.direction) { "START" -> StartScreen(gViewModel = gViewModel) "CHOOSER" -> Chooser() "GALLERY" -> ScrollItems(catalogue = gUiState.catalogItem) else -> StartScreen(gViewModel = gViewModel) } }
验证效果
修改后,点击按钮时操作的就是主Composable使用的同一个ViewModel实例,redirection更新的状态会通过StateFlow传递给collectAsState,主页面会收到状态变化、触发重组,同时打印路径变更的日志,页面也会正常切换。
内容的提问来源于stack exchange,提问作者Alberto Soligo
相关产品推荐
相关产品推荐

