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

Jetpack Compose如何实现展开BottomSheet转全屏效果

实现方案

别用旧版Material的默认BottomSheet组件,直接用Material3 1.2.0+版本自带的ModalBottomSheet就能实现,不需要引入第三方库,也不用自己写自定义布局处理手势。

核心实现逻辑

  • 组件原生就支持三态切换:隐藏、半高展开、全屏展开,之前你切不了全屏,大概率是开了半高跳过配置,或者没处理系统栏边距导致视觉上没铺满。
  • 不需要自己处理手势冲突、惯性吸附、动画插值,组件内置的逻辑已经覆盖了拖动、快速甩动切换状态的交互,和目标动效完全匹配。

核心实现代码

// 先引入对应依赖,注意不要导错Material2的包
// implementation "androidx.compose.material3:material3:1.2.1"
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomSheetDemo() {
    val screenHeight = LocalConfiguration.current.screenHeightDp.dp
    // 配置Sheet状态
    val sheetState = rememberModalBottomSheetState(
        skipPartiallyExpanded = false, // 必关:允许半展开状态存在
        confirmValueChange = { state ->
            // 可在这里加状态拦截,比如特定条件下不允许切全屏
            true
        }
    )
    val scope = rememberCoroutineScope()

    Scaffold { innerPadding ->
        // 主页面内容
        Box(modifier = Modifier.padding(innerPadding).fillMaxSize()) {
            Button(onClick = {
                scope.launch { sheetState.show() }
            }) {
                Text("拉起底部面板")
            }
        }

        if (sheetState.isVisible) {
            ModalBottomSheet(
                onDismissRequest = {
                    scope.launch { sheetState.hide() }
                },
                sheetState = sheetState,
                sheetMaxWidth = Dp.Unspecified, // 撑满屏幕宽度
                shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
                windowInsets = WindowInsets(0,0,0,0), // 清空系统栏边距,实现真·全屏铺满
                dragHandle = {
                    // 可自定义顶部拖拽条样式
                    BottomSheetDefaults.DragHandle()
                }
            ) {
                // 处理面板内部滚动和外部拖拽的手势冲突
                val nestedScrollConnection = remember(sheetState) {
                    ConsumeSwipeWithinBottomSheetBoundsNestedScrollConnection(
                        sheetState = sheetState,
                        orientation = Orientation.Vertical,
                        onFling = { velocity ->
                            // 支持快速甩动直接切换状态,不用拖到固定阈值
                            if (velocity > 1000) {
                                scope.launch { sheetState.partialExpand() }
                            } else if (velocity < -1000) {
                                scope.launch { sheetState.expand() }
                            }
                        }
                    )
                }
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .nestedScroll(nestedScrollConnection)
                        // 半高状态下占屏幕50%高度,全屏状态下自动撑满
                        .heightIn(min = screenHeight * 0.5f)
                        .padding(horizontal = 16.dp)
                ) {
                    // 半高状态显示的概要内容
                    Text("面板概要信息", modifier = Modifier.padding(vertical = 12.dp))
                    Button(onClick = {
                        // 点击按钮直接切全屏,不需要手动写动画
                        scope.launch { sheetState.expand() }
                    }) {
                        Text("查看完整详情(切换全屏)")
                    }
                    // 全屏状态下才加载显示的长内容
                    if (sheetState.targetValue == SheetValue.Expanded) {
                        repeat(20) {
                            Text("详情条目 $it", modifier = Modifier.padding(vertical = 8.dp))
                        }
                    }
                }
            }
        }
    }
}

常见踩坑点

  • 导包别出错:一定要用androidx.compose.material3.ModalBottomSheet,Material2包下的旧组件只支持隐藏/全屏两个状态,没法配置半高锚点。
  • 不要手动写偏移量动画:自己修改offset实现拖动很容易出现手势冲突、输入法顶起面板错位、状态栏适配bug,官方组件已经把这些边缘场景处理完毕。
  • 如果需要更多档位的锚点(比如小悬浮条、半高、全屏三档切换),可以用BottomSheetScaffold自定义anchors映射,把不同偏移量绑定到自定义状态即可,不需要修改组件源码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 06:24:25