Jetpack Compose Navigation:多层Bottom Sheet叠加显示方案求助
实现Compose多层Bottom Sheet堆叠方案
方法1:自定义可堆叠Bottom Sheet容器
不依赖第三方库,基于Box和AnimatedVisibility手动实现堆叠逻辑,完全控制每个Sheet的层级与状态:
- 定义Sheet状态类,管理每个Sheet的标识、可见性与内容:
data class SheetState( val id: String, val isVisible: Boolean, val content: @Composable () -> Unit )
- 编写容器组件,按渲染顺序通过
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) ) } } }
- 业务中维护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
相关产品推荐
相关产品推荐

