模态底部工作表即使初始状态为完全展开,也总是打开到部分展开状态(Compose Material3)
我正在使用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,不管传给 rememberBottomSheetState 的 initialValue 是什么。
我在源码中发现的情况
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启用 → 底部弹出层将部分展开。 - 将
PartiallyExpanded从enabledValues中移除 → 底部弹出层会完全展开,但会丢失中间的向下滑动停靠点。
尝试的变通方案
我尝试通过 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那边还没有就是否计划修复给出评论。
我建议为该问题点一个赞,以引起更多关注。