Skip to content

BottomSheet — 底部弹层 ​

从底部升上来的一层,插槽式:顶部 / 内容 / 底部三个区域。

顶部不预制任何东西。想要标题栏就把 BottomSheetHeader 传进 header 槽 —— 标题、副标题、右侧动作、关闭键、分割线都归它管。

┌──────────────────────────────┐
│ header   (可空)             │  ← 通常放 BottomSheetHeader
├──────────────────────────────┤  ← 它自带的分割线
│ content                      │  ← 内容槽:列表 / 表单 / 任意 composable
├──────────────────────────────┤
│ footer   (可空)             │  ← 底部固定区:主按钮、提示条…
└──────────────────────────────┘

基本用法 ​

标准形态(头部 + 内容):

kotlin
BottomSheet(
    onDismissRequest = onDismiss,
    header = {
        BottomSheetHeader(
            title = "选择分类",
            onDismiss = onDismiss,        // 关闭键的点击回调,自己接
            subtitle = "按用途筛选",
            actions = {                   // 「清空」这类次要动作,位于关闭键左侧
                TertiaryButton(text = "清空", onClick = { clear() })
            },
        )
    },
) {
    Column(modifier = Modifier.padding(24.dp)) {   // 表单内容:自己加内边距
        // …
    }
}

列表内容(通栏,内边距交给每一行):

kotlin
BottomSheet(
    onDismissRequest = onDismiss,
    header = { BottomSheetHeader(title = "全部分类", onDismiss = onDismiss) },
) {
    LazyColumn {
        items(categories) { item ->
            ListItem(title = item.name, onClick = { pick(item) })
        }
    }
}

底部固定一个主按钮:

kotlin
BottomSheet(
    onDismissRequest = onDismiss,
    header = { BottomSheetHeader(title = "编辑资料", onDismiss = onDismiss) },
    footer = {
        FilledButton(
            text = "保存",
            onClick = { save() },
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 24.dp, vertical = 16.dp),
        )
    },
) {
    ProfileForm()
}

不要头部:

kotlin
BottomSheet(onDismissRequest = onDismiss) {
    // 只有内容 —— header 不传就没有顶部栏,也没有那条分割线
}

参数 ​

BottomSheet ​

参数类型默认值说明
onDismissRequest() -> Unit—关闭回调。点关闭键 / 点遮罩 / 下划 / 返回键都走它
modifierModifierModifier修饰符
skipPartiallyExpandedBooleanfalsetrue 时只能「全展开」或「关闭」,没有半展开停靠点
colorsBottomSheetColorsBottomSheetDefaults.colors()配色。这里的 colors 只管容器底色
header(@Composable () -> Unit)?null顶部插槽。通常放 BottomSheetHeader;不传就没有顶部栏
footer(@Composable () -> Unit)?null底部固定槽。放主按钮、提示条这类不跟着内容滚的东西
content@Composable ColumnScope.() -> Unit—内容槽。放在最后,所以尾随 lambda 写法照常可用

BottomSheetHeader ​

参数类型默认值说明
titleString—标题,单行
onDismiss() -> Unit—关闭回调。只挂在右上角关闭键上
modifierModifierModifier修饰符
subtitleString?null副标题,单行,空白等同于不传
showCloseButtonBooleantrue是否显示关闭键
actions(@Composable () -> Unit)?null右侧动作区,位于关闭键左侧
colorsBottomSheetColorsBottomSheetDefaults.colors()配色

标题栏自带 fillMaxWidth() 和内部边距(起止 24dp / 上 20dp / 下 16dp),末尾跟一条 HorizontalDivider()。

BottomSheetColors ​

4 个槽位,分属两个组件:

字段默认值归谁
containerColorMaterialTheme.colorScheme.surfaceContainerBottomSheet
titleColorMaterialTheme.colorScheme.onSurfaceBottomSheetHeader
subtitleColorMaterialTheme.colorScheme.onSurfaceVariantBottomSheetHeader
closeIconTintMaterialTheme.colorScheme.onSurfaceVariantBottomSheetHeader

要点 ​

  • 头部一律显式传。 这里没有 title / subtitle / showCloseButton / actions 参数 —— 这四个只有 BottomSheetHeader 上有。旧代码还传着它们会编译报错,把它们挪进 header 里的 BottomSheetHeader,并把 onDismiss 接上(一般就是同一个 onDismissRequest)。

  • 没有 contentPadding,内边距自己管。 这个参数已经移除;旧代码传了会编译报错。理由:内容形态差别太大 —— 列表要通栏(内边距交给每一行,否则 24 + 16 变成双重缩进)、表单要 24dp、全宽图片要 0。给一个「万能 padding」只会让每种情况都得再覆盖一次。需要时在 content 里自己 .padding(...),一眼能看出实际值。

  • 两部分 colors 要分开传。 BottomSheet 的 colors 只管容器底色;标题文字和关闭键的颜色由传给 BottomSheetHeader 的那份决定。想一起改就传同一份:

    kotlin
    val sheetColors = BottomSheetDefaults.colors(containerColor = Color.Black)
    BottomSheet(
        colors = sheetColors,
        header = { BottomSheetHeader(title = "…", onDismiss = onDismiss, colors = sheetColors) },
    ) { /* … */ }
  • onDismiss 只挂在关闭键上。 点遮罩 / 下划 / 返回键归 BottomSheet 的 onDismissRequest 管,两者通常传同一个 lambda。

  • 内容不多时开 skipPartiallyExpanded = true。 否则默认会有「半展开」这个停靠点,内容矮的时候会停在一个尴尬的高度。

  • footer 不跟着内容滚,它是弹层的固定底栏。想让按钮跟着内容走就放进 content。

  • 标题栏的 actions 在关闭键左侧,而且两者之间自带 8dp 间距,不用自己补。

说明 ​

  • 标题栏下方那条线固定用组件库的 HorizontalDivider() 默认参数,不受 colors 控制,也没有 dividerColor 槽位。理由:分隔线本来就该比卡片描边更淡;HorizontalDivider 的默认色是 outlineVariant 叠 70% 透明度、粗细 1 物理像素,全站统一这一个规格。这里要是再开一个可配的颜色槽位,同一个界面上迟早出现两种深浅的线。
  • BottomSheetColors 和 DialogColors 是分开的两份。 底部弹窗是「从底下升上来的一层」,多一个右上角关闭键,着色点和居中弹窗并不一样 —— 合成一份会各自多出用不到的字段。
  • 形状是 AppShapes.sheetTop(只圆上边 28dp),和 heroTop 值相同但语义不同:一个说「底部弹层上沿」,一个说「折叠标题栏的内容卡片」。
  • dragHandle = null —— 关掉了 M3 自带的顶部拖拽条。有标题栏的时候那条横杠是多余的,头部本身就是可抓的地方。
  • BottomSheetHeader 可以单独用(比如在自己写的 ModalBottomSheet 里复用同一套头部),它不依赖 BottomSheet。