Skip to content

DropdownMenu — 下拉菜单 ​

锚在触发点上的浮层菜单,自带一套「文字贴左、选中右侧打勾」的项。

基本用法 ​

kotlin
var expanded by remember { mutableStateOf(false) }

Box {
    IconButton(onClick = { expanded = true }) {
        Icon(Phosphor.DotsThreeVertical, contentDescription = "更多")
    }

    DropdownMenu(
        expanded = expanded,
        onDismissRequest = { expanded = false },
        modifier = Modifier.width(DropdownMenuWidth),   // ★ 一定给宽度
    ) {
        DropdownMenuItem(text = "刷新", onClick = { refresh(); expanded = false })
        DropdownMenuItem(text = "设置", onClick = { openSettings(); expanded = false })
    }
}

带前置图标、选中态、危险项:

kotlin
DropdownMenu(expanded, onDismissRequest, Modifier.width(DropdownMenuWidth)) {
    DropdownMenuItem(
        text = "跟随系统",
        selected = mode == Mode.System,
        leadingIcon = Phosphor.CircleHalf,
        onClick = { mode = Mode.System },
    )
    DropdownMenuItem(
        text = "删除",
        contentColor = MaterialTheme.colorScheme.error,
        onClick = { delete() },
    )
}

参数 ​

参数类型默认值说明
expandedBoolean—是否展开,状态自己托管
onDismissRequest() -> Unit—点到外面 / 返回键时的回调,必须把 expanded 置回 false
modifierModifierModifier修饰符。宽度建议给 Modifier.width(DropdownMenuWidth)
offsetDpOffsetDpOffset.Zero相对锚点的偏移
colorsDropdownMenuColorsDropdownMenuDefaults.colors()容器配色
content@Composable ColumnScope.() -> Unit—菜单内容,通常放 DropdownMenuItem
参数类型默认值说明
textString—菜单项文字,单行、超长省略
onClick() -> Unit—点击回调。不会自动关闭菜单
selectedBooleanfalse选中时右侧出对勾,文字从 bodyLarge 升到 titleMedium
leadingIconImageVector?null前置图标(20dp)。只有真的传了才占位
contentColorColor?null文字与前置图标颜色;null 取 colors.contentColor
selectedIconTintColor?null对勾颜色;null 取 colors.selectedIconTint
colorsDropdownMenuItemColorsDropdownMenuItemDefaults.colors()配色

配色类 ​

类字段默认值
DropdownMenuColorscontainerColorMaterialTheme.colorScheme.surfaceContainer
DropdownMenuItemColorscontentColorMaterialTheme.colorScheme.onSurface
selectedIconTintMaterialTheme.colorScheme.onSurface
kotlin
val DropdownMenuWidth = 180.dp

要点 ​

  • 一定要给宽度。 不给的话 DropdownMenu 按内容自适应:「设置」「个人中心」这种两三个字的中文会缩成窄窄一条,看着像控件没渲染完整,而且各菜单宽度不一、右对齐后左边缘参差。全站统一 Modifier.width(DropdownMenuWidth)。
  • DropdownMenuItem 点了不会自己关菜单。 每个 onClick 里都要自己 expanded = false,漏一个就会出现「点了菜单还杵在那儿」。
  • leadingIcon 是「有才占位」。 不传时文字直接贴左,不会留一段空缩进。所以同一份菜单里可以一项带图标、一项不带 —— 只是左边缘会不齐,视觉上自己接受。
  • 选中态字号不变,只加字重。 bodyLarge 和 titleMedium 都是 16sp,只差 Normal → Medium。这是刻意的:改用 titleSmall(14sp)会让选中项看起来像换了一种字体,而不只是「重了一点」。
  • 危险项用 contentColor = MaterialTheme.colorScheme.error,不要新建一套红色配色,也不要在页面里手写色值。

说明 ​

  • 为什么同时用「表面色阶」和「投影」:这不是重复。surfaceContainer 比页面背景亮一档,深色主题下投影几乎看不见,靠它分层;shadowElevation = 6.dp 让浮层在白底上真的浮起来,浅色主题下靠它分层。两种机制各管一个主题,缺一个就会在另一个主题下失效 —— 这里曾经把 shadowElevation 设成 0,理由是「表面音阶比投影可靠」,代价是浅色下菜单变成白底上一块平的灰方块。
  • 形状取 AppShapes.cardSmall(16dp):比卡片小一档,浮层不需要那么软的边。
  • 全站的下拉菜单项只有这一种排法。 曾经首页的 ⋮ 菜单和设置页的主题模式下拉各写一遍,于是同一类浮层出现了两种观感:一边文字右边打勾、加粗;一边是左边打勾、还因为「给前置图标留 20dp 占位」而整体被顶出一截 —— 在纯文字菜单里,那个占位就是一段莫名其妙的缩进。