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

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的异常切换。


修复方案

问题根源

现有代码存在两处逻辑冲突:

  1. 当bottomSheetState变为Hidden时,触发viewModel.selectItem(null)导致selectedItem置空
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 15:45:35