Jetpack Compose 中一个 ModalBottomSheetLayout 的多个 BottomSheets [英] Multiple BottomSheets for one ModalBottomSheetLayout in Jetpack Compose

查看:235
本文介绍了Jetpack Compose 中一个 ModalBottomSheetLayout 的多个 BottomSheets的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现一个可以显示两个不同底部工作表的屏幕.由于 ModalBottomSheetLayout 只有一张纸的插槽,我决定使用 selected 动态更改 ModalBottomSheetLayoutsheetContent当我想显示两个工作表中的任何一个时的状态(

解决方案

我有一个类似的用例,我需要显示 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆