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

Jetpack Compose Navigation:多层Bottom Sheet叠加显示方案求助

实现Compose多层Bottom Sheet堆叠方案

方法1:自定义可堆叠Bottom Sheet容器

不依赖第三方库,基于Box和AnimatedVisibility手动实现堆叠逻辑,完全控制每个Sheet的层级与状态:

  1. 定义Sheet状态类,管理每个Sheet的标识、可见性与内容:
data class SheetState(
    val id: String,
    val isVisible: Boolean,
    val content: @Composable () -> Unit
)
  1. 编写容器组件,按渲染顺序通过zIndex设置层级,用动画处理进出效果:
@Composable
fun StackedBottomSheetContainer(sheets: List<SheetState>) {
    Box(modifier = Modifier.fillMaxSize()) {
        // 渲染所有Sheet,后添加的层级更高
        sheets.forEachIndexed { index, sheet ->
            AnimatedVisibility(
                visible = sheet.isVisible,
                enter = slideInVertically(initialOffsetY = { it }),
                exit = slideOutVertically(targetOffsetY = { it })
            ) {
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .align(Alignment.BottomCenter)
                        .zIndex(index.toFloat())
                ) {
                    sheet.content()
                }
            }
        }
        // 全局半透明背景,仅在有Sheet显示时展示
        if (sheets.any { it.isVisible }) {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.Black.copy(alpha = 0.5f))
                    .zIndex(-1f)
            )
        }
    }
}
  1. 业务中维护Sheet列表,通过增删实现打开/关闭:
val sheetList = remember { mutableStateListOf<SheetState>() }

Button(onClick = {
    sheetList.add(
        SheetState(
            id = "sheet1",
            isVisible = true,
            content = {
                BottomSheetContent(title = "第一层Sheet") {
                    // 打开第二层Sheet
                    sheetList.add(
                        SheetState(
                            id = "sheet2",
                            isVisible = true,
                            content = {
                                BottomSheetContent(title = "第二层Sheet") {
                                    sheetList.removeAll { it.id == "sheet2" }
                                }
                            }
                        )
                    )
                }
            }
        )
    )
}) {
    Text("打开多层Sheet")
}

StackedBottomSheetContainer(sheets = sheetList)

// 通用Sheet内容组件
@Composable
fun BottomSheetContent(title: String, onClose: () -> Unit) {
    Surface(
        modifier = Modifier.fillMaxWidth(),
        shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
    ) {
        Column(
            modifier = Modifier.padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(title, fontSize = 18.sp, fontWeight = FontWeight.Bold)
            Spacer(modifier = Modifier.height(16.dp))
            Button(onClick = onClose) {
                Text("关闭")
            }
        }
    }
}

方法2:基于官方ModalBottomSheetLayout改造栈逻辑

官方组件本身仅支持单个Sheet,但可通过维护Sheet栈、动态切换内容模拟堆叠:

val sheetStack = remember { mutableStateListOf<@Composable () -> Unit>() }
val currentSheet = remember { derivedStateOf { sheetStack.lastOrNull() } }
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val coroutineScope = rememberCoroutineScope()

ModalBottomSheetLayout(
    sheetState = sheetState,
    sheetContent = { currentSheet.value?.invoke() ?: Box {} },
    // 根据栈的长度调整背景透明度,模拟堆叠层次感
    scrimColor = if (sheetStack.isNotEmpty()) Color.Black.copy(alpha = 0.3f * sheetStack.size) else Color.Transparent
) {
    Button(onClick = {
        sheetStack.add {
            BottomSheetContent(title = "第一层") {
                sheetStack.add {
                    BottomSheetContent(title = "第二层") {
                        sheetStack.removeLast()
                        coroutineScope.launch { sheetState.show() }
                    }
                }
                coroutineScope.launch { sheetState.show() }
            }
        }
        coroutineScope.launch { sheetState.show() }
    }) {
        Text("打开Sheet栈")
    }
}

方法3:用Dialog组件模拟Bottom Sheet

利用Dialog天然支持多实例的特性,设置底部定位并通过zIndex控制层级:

val dialogStates = remember { mutableStateListOf<Pair<String, Boolean>>() }

Button(onClick = {
    dialogStates.add("dialog1" to true)
}) {
    Text("打开Dialog式Sheet")
}

dialogStates.forEachIndexed { index, (id, isVisible) ->
    if (isVisible) {
        Dialog(
            onDismissRequest = { dialogStates[index] = id to false },
            properties = DialogProperties(usePlatformDefaultWidth = false)
        ) {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .align(Alignment.BottomCenter)
                    .zIndex(index.toFloat())
            ) {
                BottomSheetContent(title = "第${index+1}层Sheet") {
                    dialogStates[index] = id to false
                }
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Phehaz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 17:25:31