Skip to content

AlertDialog — 弹窗 ​

三槽插槽式:标题、内容、底部三个区域都由调用方决定放什么。组件本身只负责外观与关闭行为,不做任何按钮数量、文案、排布的约定。

旧的便捷式(SimpleAlertDialog,以及 title: String / text: String / confirmText 那一套重载)已删除。标题由调用方自己传 DialogTitle。

┌──────────────────────────┐
│ title     (可空)        │  ← 常放 DialogTitle;也可放自定义头部
│ content                  │  ← 正文:文本 / 输入框 / 列表 / 滚动容器…
│ bottom    (可空)        │  ← 底部动作区:横排、竖排堆叠都行
└──────────────────────────┘

基本用法 ​

两个按钮(最常见的确认弹窗):

kotlin
AlertDialog(
    onDismissRequest = onDismiss,
    title = { DialogTitle("退出登录") },
    content = { Text("退出后,收藏与最近使用记录仍保留在本机。") },
    bottom = {
        DialogButtonRow {
            TertiaryButton(text = "取消", onClick = onDismiss)
            FilledButton(text = "退出", onClick = onConfirm)
        }
    },
)

三个以上按钮直接竖排(不要硬塞进 DialogButtonRow):

kotlin
AlertDialog(
    onDismissRequest = onDismiss,
    title = { DialogTitle("有新版本", supportingText = "本次更新修复了若干问题") },
    content = { Text("建议现在更新,也可以稍后在设置里手动检查。") },
    bottom = {
        FilledButton(text = "立即更新", onClick = onUpdate, modifier = Modifier.fillMaxWidth())
        TonalButton(text = "稍后提醒", onClick = onLater, modifier = Modifier.fillMaxWidth())
        TertiaryButton(text = "跳过此版本", onClick = onSkip, modifier = Modifier.fillMaxWidth())
    },
)

没有标题、内容要滚动:

kotlin
AlertDialog(
    onDismissRequest = onDismiss,
    content = {
        Column(
            modifier = Modifier
                .heightIn(max = 360.dp)
                .verticalScroll(rememberScrollState()),
        ) {
            Text(agreementText)
        }
    },
    bottom = { DialogButtonRow { FilledButton(text = "同意", onClick = onAgree) } },
)

强制类弹窗(不允许返回键 / 点击外部关闭):

kotlin
AlertDialog(
    onDismissRequest = { },
    dismissOnBackPress = false,
    dismissOnClickOutside = false,
    title = { DialogTitle("强制更新") },
    content = { Text("不更新将无法继续使用。") },
    bottom = { FilledButton(text = "立即更新", onClick = onUpdate, modifier = Modifier.fillMaxWidth()) },
)

参数 ​

AlertDialog ​

参数类型默认值说明
onDismissRequest() -> Unit—请求关闭(返回键 / 点击外部)
modifierModifierModifier外层修饰符
dismissOnBackPressBooleantrue返回键是否可关闭(强制类弹窗传 false)
dismissOnClickOutsideBooleantrue点击弹窗外部是否可关闭
contentPaddingPaddingValues起止 24dp / 上 8dp / 下 20dp内容区内边距
bottomPaddingPaddingValues起止 24dp / 上 8dp / 下 24dp底部区内边距
colorsDialogColorsDialogDefaults.colors()配色
title(@Composable ColumnScope.() -> Unit)?null标题槽(可空)
content@Composable ColumnScope.() -> Unit{}内容槽
bottom(@Composable ColumnScope.() -> Unit)?null底部槽(可空)

三个槽都是 ColumnScope。 横排竖排由调用方自己决定。

三个槽放什么 ​

槽典型内容
titleDialogTitle(text = "标题");不需要标题栏就不传(标题区连内边距一起消失)
content一段 Text、一个 TextField、一个可滚动的 Column……
bottom「取消 + 确认」两个按钮横排;按钮多时依次排开就是竖排

DialogTitle ​

参数类型默认值说明
textString—标题,用 headlineSmall
supportingTextString?null标题下方补充说明(bodySmall),空白等同于不传
modifierModifierModifier修饰符
colorsDialogColorsDialogDefaults.colors()配色

标题与说明之间是 6dp。

DialogButtonRow ​

参数类型默认值说明
modifierModifierModifier修饰符。内部会 fillMaxWidth()
content@Composable RowScope.() -> Unit—按钮。内部是 RowScope

排布是 Arrangement.spacedBy(8.dp, Alignment.End) —— 右对齐、间距 8dp。

DialogColors ​

字段默认值
containerColorMaterialTheme.colorScheme.surfaceContainerHigh
titleColorMaterialTheme.colorScheme.onSurface
supportingTextColorMaterialTheme.colorScheme.onSurfaceVariant
contentColorMaterialTheme.colorScheme.onSurfaceVariant

没有按钮色 —— 按钮自己取色(FilledButton / TertiaryButton 那套层级体系),弹窗再给一份会和它打架。

要点 ​

  • Modifier.weight 需要 RowScope。 想给按钮分配宽度,必须包在 DialogButtonRow 里;直接写在 bottom 里编译不过(那是 ColumnScope)。
  • 不要标题就别传 title。 不传时标题区连同它那 24 / 12 的内边距一起消失,不会留一块空洞。
  • 三个以上按钮不要用 DialogButtonRow。 在 bottom 里依次排开即可 —— ColumnScope 天然竖排,每个按钮 Modifier.fillMaxWidth() 占满一行,点击区更大、层级也更清楚。
  • onDismissRequest 是「返回键 / 点击外部」的回调,和 bottom 里按钮的 onClick 不是一回事。按钮点「取消」时通常自己调同一个 lambda。
  • content 有默认值 {},所以可以只写标题和底部;但 onDismissRequest 必传。
  • 内容很长时自己给高度上限 + 滚动(heightIn(max = …) + verticalScroll),弹窗本身不滚。
  • 单个按钮也可以放在 bottom 里,ColumnScope 会把它按左对齐的整宽块排;想靠右就用 DialogButtonRow。

说明 ​

  • 容器色是 surfaceContainerHigh,形状是 MaterialTheme.shapes.extraLarge(32dp)。 M3 里对话框是「浮在内容之上的高海拔表面」,用更高的表面音阶而不是 16dp 投影来表达层级 —— 深色主题下纯 surface 的对话框会和背景糊在一起,看不出是弹层。所以这里 tonalElevation 和 shadowElevation 都显式设成 0。
  • 宽度是 fillMaxWidth(0.86f) + widthIn(max = 420.dp):手机上左右各留 7% 边距,平板上不会拉成一整条。它用的是 usePlatformDefaultWidth = false —— 平台默认宽度太窄,这里自己接管。
  • DialogTitle 是「标准标题」,不是必须的。 title 槽是自由的 —— 想放自定义头部(比如带图标的标题行)就直接放,不受 DialogTitle 限制。
  • DialogButtonRow 只是语法糖,存在的唯一理由是省掉「每个两按钮弹窗都手写一遍右对齐 Row」。
  • 头部 / 内容 / 底部是三段独立的内边距,不是一个大 padding 包住:头部 24/12、内容区按 contentPadding、底部按 bottomPadding。这样头部挨着内容、底部动作区自己留白,中间不会出现双份间距。