模态底部工作表即使初始状态为完全展开,也总是打开到部分展开状态(Compose Material3)

移动开发 2026-07-08

我正在使用Compose Multiplatform Material3 (org.jetbrains.compose.material3:material3:1.12.0-alpha02),并希望得到一个 ModalBottomSheet,它:

预期行为

  • 打开: Hidden → Expanded
  • 向下滑动: Expanded → PartiallyExpanded → Hidden

换句话说,底部弹出层应立即完全展开,但在用户解除时仍应允许 PartiallyExpanded 作为一个中间停靠点。

当前设置

val sheetState = rememberBottomSheetState(
    initialValue = SheetValue.Expanded,
    enabledValues = setOf(
        SheetValue.Hidden,
        SheetValue.PartiallyExpanded,
        SheetValue.Expanded,
    ),
)

ModalBottomSheet(
    sheetState = sheetState,
    onDismissRequest = { /* ... */ },
) {
    // content
}

实际行为

底部弹出层总是先打开到 PartiallyExpanded,不管传给 rememberBottomSheetStateinitialValue 是什么。


我在源码中发现的情况

ModalBottomSheet 在内部执行:

if (sheetState.hasExpandedState) {
    LaunchedEffect(sheetState) {
        sheetState.show()
    }
}

show() 内:

suspend fun show() {
    val targetValue = when {
        hasPartiallyExpandedState -> PartiallyExpanded
        else -> Expanded
    }
    animateTo(targetValue, showMotionSpec)
}

由于 PartiallyExpanded 已启用,show() 总是指向它并忽略 initialValue

这意味着:

  • 保持 PartiallyExpanded 启用 → 底部弹出层将部分展开。
  • PartiallyExpandedenabledValues 中移除 → 底部弹出层会完全展开,但会丢失中间的向下滑动停靠点。

尝试的变通方案

我尝试通过 confirmValueChange 拦截过渡,并将初始的 show() 调用重定向到 Expanded

val scope = rememberCoroutineScope()

val sheetStateRef = remember { mutableStateOf<SheetState?>(null) }
val shouldExpandOnOpen = remember { mutableStateOf(true) }

val sheetState = rememberBottomSheetState(
    initialValue = SheetValue.Hidden,
    enabledValues = setOf(
        SheetValue.Hidden,
        SheetValue.PartiallyExpanded,
        SheetValue.Expanded
    ),
    confirmValueChange = remember {
        { newValue: SheetValue ->
            when {
                newValue == SheetValue.PartiallyExpanded &&
                    shouldExpandOnOpen.value -> {
                    scope.launch {
                        sheetStateRef.value?.expand()
                    }
                    false
                }

                newValue == SheetValue.Expanded -> {
                    shouldExpandOnOpen.value = false
                    true
                }

                newValue == SheetValue.Hidden -> {
                    shouldExpandOnOpen.value = true
                    true
                }

                else -> true
            }
        }
    }
)

SideEffect {
    sheetStateRef.value = sheetState
}

之所以有效,是因为 confirmValueChange 会在动画开始前从 show() 同步调用。

然而,这看起来像是在滥用 confirmValueChange,它的用途似乎是做校验而不是重定向过渡。


问题

是否存在官方或更清晰的方式,使 ModalBottomSheet 能直接打开到 Expanded,同时仍然保留 PartiallyExpanded 作为向下滑动的停靠点?

或者这在Material3 API中目前就是一个限制/缺失的特性?

版本: org.jetbrains.compose.material3:material3:1.12.0-alpha02

解决方案

这在Material3 API中目前是否为限制/缺失的特性?

请查看Google Issue Tracker上的 此问题——是的,似乎确实是API的一个限制,到目前为止Google那边还没有就是否计划修复给出评论。
我建议为该问题点一个赞,以引起更多关注。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章