Jetpack Compose多ModalBottomSheet状态变更时未更新问题
问题原因
你把底部弹窗的Composable内容存进了mutableStateOf类型的bottomSheetContent变量中,点击按钮赋值时,lambda捕获的是赋值当下的screenState值。后续screenState更新时,已经存入状态的Composable lambda不会自动订阅状态变更,也不会重新生成新的实例,因此弹窗内部拿不到最新的选中状态,不会触发重组。
主页面的代码直接在MainScreen的Composition作用域中读取screenState,状态变化时会正常触发重组,因此能显示最新值。
修复方案
优先推荐第一种实现方式,逻辑更清晰,也符合Compose状态驱动UI的设计原则。
方案1:用状态标记弹窗类型,直接在sheetContent中渲染UI
- 首先定义密封类标记当前要展示的弹窗类型:
sealed class BottomSheetType { object None : BottomSheetType() object First : BottomSheetType() object Second : BottomSheetType() }
- 替换原来存储Composable内容的状态,改为存储当前弹窗类型:
// 替换掉原来的bottomSheetContent状态 var currentBottomSheet by remember { mutableStateOf<BottomSheetType>(BottomSheetType.None) }
- 修改
ModalBottomSheetLayout的sheetContent逻辑,直接根据当前弹窗类型渲染对应内容,不要使用预存的Composable lambda:
sheetContent = { Box( modifier = Modifier.defaultMinSize(minHeight = 1.dp) ) { when (currentBottomSheet) { BottomSheetType.First -> FirstBottomSheet( selectedText = screenState.selectedTextFromFirstBottomSheet, onSelected = { text -> viewModel.onEvent(MainScreenEvent.OnFirstBottomSheetSelectedTextChanged(text)) }, textList = screenState.firstBottomSheetTextList ) BottomSheetType.Second -> SecondBottomSheet( selectedText = screenState.selectedTextFromSecondBottomSheet, onSelected = { text -> viewModel.onEvent(MainScreenEvent.OnSecondBottomSheetSelectedTextChanged(text)) }, textList = screenState.secondBottomSheetTextList ) BottomSheetType.None -> Unit } } }
- 修改两个按钮的点击逻辑,不再赋值Composable内容,只更新当前弹窗类型再唤起弹窗:
// 第一个按钮点击逻辑 Button(onClick = { currentBottomSheet = BottomSheetType.First scope.launch { bottomSheetState.show() } }, modifier = Modifier.padding(16.dp)) { Text(text = " Open First BottomSheet") } // 第二个按钮点击逻辑 Button(onClick = { currentBottomSheet = BottomSheetType.Second scope.launch { bottomSheetState.show() } }, modifier = Modifier.padding(16.dp)) { Text(text = " Open Second BottomSheet") }
可以额外加一个状态监听,弹窗隐藏时重置状态:
LaunchedEffect(bottomSheetState.currentValue) { if (bottomSheetState.currentValue == ModalBottomSheetValue.Hidden) { currentBottomSheet = BottomSheetType.None } }
方案2:监听状态变化,同步更新存储的Composable内容
如果你一定要保留存储Composable实例的写法,需要在screenState变化时重新给bottomSheetContent赋值,保证lambda捕获的是最新状态。这种写法冗余度更高,不推荐使用:
// 同样需要先定义弹窗类型状态 var currentBottomSheet by remember { mutableStateOf<BottomSheetType>(BottomSheetType.None) } // 监听screenState变化,同步更新弹窗内容 LaunchedEffect(screenState, currentBottomSheet) { bottomSheetContent = when (currentBottomSheet) { BottomSheetType.First -> { { FirstBottomSheet( selectedText = screenState.selectedTextFromFirstBottomSheet, onSelected = { text -> viewModel.onEvent(MainScreenEvent.OnFirstBottomSheetSelectedTextChanged(text)) }, textList = screenState.firstBottomSheetTextList ) } } BottomSheetType.Second -> { { SecondBottomSheet( selectedText = screenState.selectedTextFromSecondBottomSheet, onSelected = { text -> viewModel.onEvent(MainScreenEvent.OnSecondBottomSheetSelectedTextChanged(text)) }, textList = screenState.secondBottomSheetTextList ) } } BottomSheetType.None -> null } }
按钮点击逻辑和方案1一致,只更新currentBottomSheet再唤起弹窗即可。
内容的提问来源于stack exchange,提问作者Teo Coding
相关产品推荐
相关产品推荐

