Jetpack Compose 中一个 ModalBottomSheetLayout 的多个 BottomSheets [英] Multiple BottomSheets for one ModalBottomSheetLayout in Jetpack Compose
问题描述
我想实现一个可以显示两个不同底部工作表的屏幕.由于 ModalBottomSheetLayout
只有一张纸的插槽,我决定使用 selected
动态更改 ModalBottomSheetLayout
的 sheetContent
当我想显示两个工作表中的任何一个时的状态(
我有一个类似的用例,我需要显示 2-3 个堆叠的底层.我最终复制了大部分 Compose BottomSheet 并添加了所需的行为:
enum class BottomSheetValue { SHOWING, HIDDEN }@可组合有趣的BottomSheet(parentHeight: Int,顶部偏移:Dp = 0.dp,fillMaxHeight: Boolean = false,sheetState: SwipeableState,形状:形状 = bottomSheetShape,backgroundColor: Color = MaterialTheme.colors.background,contentColor: 颜色 = contentColorFor(backgroundColor),海拔:Dp = 0.dp,内容:@Composable() ->单元){val topOffsetPx = with(LocalDensity.current) { topOffset.roundToPx() }var bottomSheetHeight 通过记住 { mutableStateOf(parentHeight.toFloat())}val scrollConnection = sheetState.PreUpPostDownNestedScrollConnection底部表格布局(maxHeight = parentHeight - topOffsetPx,填充最大高度 = 填充最大高度){val swipeable = Modifier.swipeable(状态 = sheetState,锚点 = mapOf(parentHeight.toFloat() 到 BottomSheetValue.HIDDEN,parentHeight - bottomSheetHeight 到 BottomSheetValue.SHOWING),方向 = 方向.垂直,阻力 = 零)表面(形状 = 形状,颜色 = 背景颜色,内容颜色 = 内容颜色,海拔 = 海拔,修饰符 = 修饰符.nestedScroll(scrollConnection).offset { IntOffset(0, sheetState.offset.value.roundToInt()) }.then(可滑动).onGloballyPositioned {bottomSheetHeight = it.size.height.toFloat()},){内容()}}}@可组合私人乐趣BottomSheetLayout(最大高度:整数,fillMaxHeight:布尔值,内容:@Composable() ->单元){布局(内容=内容){可测量,约束->val sheetConstraints =如果(填充最大高度){约束.copy(minHeight = maxHeight, maxHeight = maxHeight)} 别的 {约束.copy(maxHeight = maxHeight)}val placeable = measurables.first().measure(sheetConstraints)布局(可放置的宽度,可放置的高度){placeable.placeRelative(0, 0)}}}
TopOffset 例如允许将 bottomSheet 放在 AppBar 下方:
BoxWithConstraints {底片(parentHeight = 约束.maxHeight,topOffset = with(LocalDensity.current) {56.toDp()}fillMaxHeight = 真,sheetState = yourSheetState,){内容()}}
I want to implement a screen which can show two different bottom sheets.
Since ModalBottomSheetLayout
only has a slot for one sheet I decided to change the sheetContent
of the ModalBottomSheetLayout
dynamically using a selected
state when I want to show either of the two sheets (full code).
val sheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
val (selected, setSelected) = remember(calculation = { mutableStateOf(0) })
ModalBottomSheetLayout(sheetState = sheetState, sheetContent = {
when (selected) {
0 -> Layout1()
1 -> Layout2()
}
}) {
Content(sheetState = sheetState, setSelected = setSelected)
}
This works fine for very similar sheets, but as soon as you add more complexity to either of the two sheet layouts the sheet will not show when the button is pressed for the first time, it will only show after the button is pressed twice as you can see here:
Here you can find a reproducible example
I had a similar usecase, where I needed to show 2-3 stacked bottomsheets. I ended up copying large part of Compose BottomSheet and added the desired behavior:
enum class BottomSheetValue { SHOWING, HIDDEN }
@Composable
fun BottomSheet(
parentHeight: Int,
topOffset: Dp = 0.dp,
fillMaxHeight: Boolean = false,
sheetState: SwipeableState<BottomSheetValue>,
shape: Shape = bottomSheetShape,
backgroundColor: Color = MaterialTheme.colors.background,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = 0.dp,
content: @Composable () -> Unit
) {
val topOffsetPx = with(LocalDensity.current) { topOffset.roundToPx() }
var bottomSheetHeight by remember { mutableStateOf(parentHeight.toFloat())}
val scrollConnection = sheetState.PreUpPostDownNestedScrollConnection
BottomSheetLayout(
maxHeight = parentHeight - topOffsetPx,
fillMaxHeight = fillMaxHeight
) {
val swipeable = Modifier.swipeable(
state = sheetState,
anchors = mapOf(
parentHeight.toFloat() to BottomSheetValue.HIDDEN,
parentHeight - bottomSheetHeight to BottomSheetValue.SHOWING
),
orientation = Orientation.Vertical,
resistance = null
)
Surface(
shape = shape,
color = backgroundColor,
contentColor = contentColor,
elevation = elevation,
modifier = Modifier
.nestedScroll(scrollConnection)
.offset { IntOffset(0, sheetState.offset.value.roundToInt()) }
.then(swipeable)
.onGloballyPositioned {
bottomSheetHeight = it.size.height.toFloat()
},
) {
content()
}
}
}
@Composable
private fun BottomSheetLayout(
maxHeight: Int,
fillMaxHeight: Boolean,
content: @Composable () -> Unit
) {
Layout(content = content) { measurables, constraints ->
val sheetConstraints =
if (fillMaxHeight) {
constraints.copy(minHeight = maxHeight, maxHeight = maxHeight)
} else {
constraints.copy(maxHeight = maxHeight)
}
val placeable = measurables.first().measure(sheetConstraints)
layout(placeable.width, placeable.height) {
placeable.placeRelative(0, 0)
}
}
}
TopOffset e.g. allows to place the bottomSheet below the AppBar:
BoxWithConstraints {
BottomSheet(
parentHeight = constraints.maxHeight,
topOffset = with(LocalDensity.current) {56.toDp()}
fillMaxHeight = true,
sheetState = yourSheetState,
) {
content()
}
}
这篇关于Jetpack Compose 中一个 ModalBottomSheetLayout 的多个 BottomSheets的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!