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

如何在Android Material 3 Jetpack Compose中实现BottomSheet

Material 3 版本Jetpack Compose 实现BottomSheet正确方案

Material 3对BottomSheet能力做了场景拆分,早期1.2.0以下版本确实没有对应M2的BottomSheetScaffold,目前官方已经按使用场景提供了对应实现,不需要额外自定义复杂逻辑:

  • 前置准备:将Material3依赖升级到androidx.compose.material3:material3:1.2.0及以上正式版本,低版本缺失很多BottomSheet相关的官方适配,自定义实现很容易出现手势冲突、适配问题。

场景1:模态底部弹层(弹出时带半透明遮罩,阻断背后页面交互)

这类是最常用的场景,比如弹出操作菜单、选择面板、信息展示卡,直接使用官方提供的ModalBottomSheet组件即可。
基础实现代码:

// 控制弹层显示状态
val showSheet = rememberSaveable { mutableStateOf(false) }
// 弹层拖拽状态,支持自定义初始展开状态、动画规格
val sheetState = rememberModalBottomSheetState(
    initialValue = ModalBottomSheetValue.Hidden
)
val coroutineScope = rememberCoroutineScope()

// 页面主框架
Scaffold { innerPadding ->
    Column(
        modifier = Modifier
            .padding(innerPadding)
            .fillMaxSize()
            .padding(16.dp)
    ) {
        Button(onClick = { showSheet.value = true }) {
            Text("打开底部弹层")
        }
    }
}

// 底部弹层
if (showSheet.value) {
    ModalBottomSheet(
        onDismissRequest = { showSheet.value = false },
        sheetState = sheetState,
        // 可选配置:弹层形状、背景色、窗口Insets等
        shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
    ) {
        // 弹层内部内容,支持任意Compose组件
        Text(
            text = "弹层标题",
            style = MaterialTheme.typography.titleLarge,
            modifier = Modifier.padding(horizontal = 16.dp)
        )
        Spacer(modifier = Modifier.height(16.dp))
        repeat(5) { index ->
            ListItem(
                headlineContent = { Text("选项 ${index + 1}") },
                modifier = Modifier.clickable {
                    coroutineScope.launch { sheetState.hide() }.invokeOnCompletion {
                        if (!sheetState.isVisible) showSheet.value = false
                    }
                }
            )
        }
        // 适配系统导航栏,避免内容被遮挡
        Spacer(modifier = Modifier.navigationBarsPadding())
    }
}

场景2:常驻底部面板(和页面内容同层级,无遮罩,支持拖拽半展开/全展开/收起)

这类就是M2版本BottomSheetScaffold覆盖的场景,比如地图页的底部信息栏、播放器的半透明控制面板,M3 1.2.0+版本已经重新提供了同名的BottomSheetScaffold组件,API和M2版本基本对齐,迁移成本极低。
基础实现代码:

val scaffoldState = rememberBottomSheetScaffoldState()
val coroutineScope = rememberCoroutineScope()

BottomSheetScaffold(
    scaffoldState = scaffoldState,
    sheetPeekHeight = 120.dp, // 收起状态下露出的面板高度
    sheetShape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
    sheetContent = {
        // 底部面板内容
        Column(Modifier.fillMaxWidth()) {
            Text(
                text = "常驻底部面板",
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier.padding(16.dp)
            )
            // 面板内可直接放滚动组件,默认支持嵌套滚动,拖拽手势自动衔接
            LazyColumn {
                items(20) { index ->
                    ListItem(headlineContent = { Text("列表项 ${index + 1}") })
                }
                item {
                    Spacer(modifier = Modifier.navigationBarsPadding())
                }
            }
        }
    }
) { innerPadding ->
    // 页面主内容区
    Box(
        modifier = Modifier
            .padding(innerPadding)
            .fillMaxSize()
    ) {
        Button(
            onClick = {
                coroutineScope.launch {
                    if (scaffoldState.bottomSheetState.isVisible) {
                        scaffoldState.bottomSheetState.hide()
                    } else {
                        scaffoldState.bottomSheetState.expand()
                    }
                }
            },
            modifier = Modifier.align(Alignment.Center)
        ) {
            Text("切换底部面板状态")
        }
    }
}

注意事项:

  • 导入组件时请确认包名为androidx.compose.material3,不要误导入M2包下的同名组件导致样式、行为异常
  • 不要在低版本M3库上自己实现BottomSheet拖拽逻辑,很容易出现嵌套滚动冲突、Insets适配错误、动画卡顿等问题,升级官方稳定版依赖是最优解
  • 如果需要实现底部弹层强制不允许拖拽关闭,可在rememberModalBottomSheetState中传入confirmValueChange参数拦截状态变更即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 21:33:24