Material3 BottomSheetScaffold关联选中项状态异常修复咨询
Material3 BottomSheetScaffold 状态异常切换修复方案
需求与问题背景
核心需求
- 选中项目时打开BottomSheet展示内容
- 未选中项目时BottomSheet关闭
- 用户手动关闭BottomSheet时取消选中状态
- 用户展开BottomSheet后,下次选中项目时保留该展开状态
现有代码
val selectedItem by viewModel.selectedItem.collectAsState() val bottomSheetState = rememberStandardBottomSheetState( initialValue = SheetValue.Hidden, skipHiddenState = false ) LaunchedEffect(bottomSheetState.currentValue) { if (bottomSheetState.currentValue == SheetValue.Hidden ) { viewModel.selectItem(null) } } if (selectedItem == null) { LaunchedEffect(bottomSheetState.currentValue) { bottomSheetState.hide() } } val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(bottomSheetState) BottomSheetScaffold( modifier = Modifier.navigationBarsPadding().imePadding(), scaffoldState = bottomSheetScaffoldState, sheetPeekHeight = Dimens.bottomSheetPeekHeight, sheetContainerColor = MaterialTheme.colorScheme.background, sheetContent = { selectedItem?.let { ItemView(it) } }, )
当前Bug表现
用户关闭BottomSheet后代码重复执行,BottomSheet状态出现Hidden→Hidden→PartiallyExpanded→Hidden的异常切换。
修复方案
问题根源
现有代码存在两处逻辑冲突:
- 当
bottomSheetState变为Hidden时,触发viewModel.selectItem(null)导致selectedItem置空 selectedItem为空时,又启动LaunchedEffect调用bottomSheetState.hide(),此时状态已是Hidden,重复调用触发不必要的回调,引发循环与异常状态切换
优化实现
修复后的完整代码
val selectedItem by viewModel.selectedItem.collectAsState() // 保存用户最后手动设置的非隐藏状态,用于下次选中时恢复 val lastExpandedState = remember { mutableStateOf<SheetValue>(SheetValue.PartiallyExpanded) } val bottomSheetState = rememberStandardBottomSheetState( initialValue = SheetValue.Hidden, skipHiddenState = false ) // 监听BottomSheet状态变化,处理手动关闭后的选中状态取消,同时记录用户调整的状态 LaunchedEffect(bottomSheetState.currentValue) { val currentState = bottomSheetState.currentValue // 仅当状态从非Hidden变为Hidden时,才取消选中(避免重复触发) if (currentState == SheetValue.Hidden && bottomSheetState.previousValue != SheetValue.Hidden) { viewModel.selectItem(null) } // 记录用户手动设置的非隐藏状态 if (currentState != SheetValue.Hidden) { lastExpandedState.value = currentState } } // 监听选中项变化,控制BottomSheet显示/恢复状态 LaunchedEffect(selectedItem) { selectedItem?.let { // 选中项目时,恢复用户上次的展开状态 if (bottomSheetState.currentValue != lastExpandedState.value) { bottomSheetState.animateTo(lastExpandedState.value) } } ?: run { // 未选中项目时,隐藏BottomSheet(仅当前状态不是Hidden时执行) if (bottomSheetState.currentValue != SheetValue.Hidden) { bottomSheetState.hide() } } } val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(bottomSheetState) BottomSheetScaffold( modifier = Modifier.navigationBarsPadding().imePadding(), scaffoldState = bottomSheetScaffoldState, sheetPeekHeight = Dimens.bottomSheetPeekHeight, sheetContainerColor = MaterialTheme.colorScheme.background, sheetContent = { selectedItem?.let { ItemView(it) } }, )
关键优化点
- 通过
bottomSheetState.previousValue判断状态变更的来源,避免重复触发取消选中逻辑 - 新增
lastExpandedState记录用户手动调整的展开状态,满足“下次选中保留展开状态”的需求 - 所有状态变更操作添加当前状态判断,避免重复调用
hide()或animateTo()引发异常状态切换 - 将选中项与BottomSheet状态的同步逻辑拆分为两个独立
LaunchedEffect,分别监听各自触发源,逻辑清晰无循环依赖
内容的提问来源于stack exchange,提问作者Psijic
相关产品推荐
相关产品推荐

