外观
PopupPrompt — 轻提示
全局单例的轻提示。不占布局,从顶部滑入,过一会儿自己走。
基本用法
kotlin
PopupPrompt.success("已保存到相册")
PopupPrompt.error("复制失败")
PopupPrompt.warning("网络不稳定")
PopupPrompt.info("已复制到剪贴板")长时间操作(必须自己收):
kotlin
PopupPrompt.loading("正在生成…")
generate(onDone = { PopupPrompt.dismiss() })自定义时长 / 直接给类型:
kotlin
PopupPrompt.show("已加入队列", duration = 1500L)
PopupPrompt.show("稍后重试", type = PromptType.WARNING, duration = 6000L)要读当前提示状态时(少见):
kotlin
val current by PopupPromptManager.promptState // PromptData?,null 表示没有参数
PopupPrompt —— 对外入口(全是静态方法)
| 方法 | 签名 | 说明 |
|---|---|---|
success | (message: String, duration: Long = 4000L) | 成功 |
error | (message: String, duration: Long = 4000L) | 错误 |
info | (message: String, duration: Long = 4000L) | 中性信息 |
warning | (message: String, duration: Long = 4000L) | 警告 |
loading | (message: String) | 加载中。不自动关闭,也传不了时长 |
dismiss | () | 手动收掉当前提示 |
show | (message: String, type: PromptType = PromptType.INFO, duration: Long = 4000L) | 要自定义类型或时长时用 |
PopupPromptManager
方法集与 PopupPrompt 完全一致(show / success / error / info / warning / loading / dismiss),另外多一个只读状态:
| 成员 | 类型 | 说明 |
|---|---|---|
promptState | State<PromptData?> | 当前提示,null 表示没有 |
日常用不到它 —— 只有要读当前状态时才需要。
PopupPrompt() —— 渲染容器
@Composable fun PopupPrompt() 负责把当前提示画出来。HuaLuoTheme 内部已经挂了一次,通常不用调;只有自己搭主题、不经过 HuaLuoTheme 时才在界面根部手动挂一次。
PromptType / PromptData
PromptType | 图标 | 说明 |
|---|---|---|
SUCCESS | Phosphor.CheckCircle | 成功 |
ERROR | Phosphor.XCircle | 错误 |
WARNING | Phosphor.Warning | 警告 |
INFO | Phosphor.Info | 中性信息 |
LOADING | 波浪指示器 | 加载中(不自动关闭) |
PromptData(message: String, type: PromptType = PromptType.INFO, duration: Long = 4000L)
要点
loading必须自己dismiss()。 它把duration设成Long.MAX_VALUE并且直接返回,不走自动关闭那条路 —— 忘了收就是一条永远挂在顶部的提示。- 同一时刻只有一个提示。 新的会顶掉旧的,不是排队。所以别用它承载需要逐条读的信息流。
PopupPrompt和PopupPromptManager方法集一样,日常用前者即可。- 别自己造 Snackbar / Toast。 用这一套,位置、动效、语义色才是统一的。
duration单位是毫秒,默认 4000。传太小(比如 300)会几乎看不见 —— 退出动画本身还要 300ms。- 别依赖「提示会挡住点击」:承载它的
Box是全屏的,但里面只有提示本身那一块Surface。它不拦截触摸,所以它下方的内容照常可点。
说明
- 为什么必须是反色底:浅色主题里
secondaryContainer和页面背景几乎同色,提示飘上去等于「消失」。改用inverseSurface之后:白底页面 → 深色浮层,层级一眼可辨;深色主题下自动反转成浅色浮层,两态都成立。 - 语义色也跟着一起反色,否则等于「深色图标压在深色底上」。这里曾经用过给与主题同向的表面准备的那组色:浅色主题的深色语义色压在反色后的深色底上几乎看不见,深色主题的浅色语义色压在浅色底上同样糊成一片 —— 「成功」和「错误」两个提示看起来都没颜色,就是「主题搞反了」的观感。M3 没有
inverseSuccess/inverseError这类槽位,所以取各语义对应的容器槽位(inversePrimary/errorContainer/tertiaryContainer):容器色天生是「与所在主题表面反向」的柔和色,两边都成立。 - 动效走
MaterialTheme.motionScheme的 spatial 规格,而不是写死tween(300)—— 提示是「会改变位置」的元素,正该用 spatial。滑入叠加fadeIn(220),滑出叠加fadeOut(180)。 - 关闭时序:到时长后先播退出动画,再等 300ms 才真正清空状态。所以「上一条消失」和「下一条出现」之间是干净的。
- 位置固定在顶部居中:
padding(horizontal = 20.dp, vertical = 72.dp)、fillMaxWidth()、zIndex(1000f)。72dp 的上边距是为了避开状态栏和标题栏。 - 形状是
AppShapes.cardSmall(16dp),shadowElevation = 0—— 反色底已经足够表达层级,再加投影会显脏。 - 命名变更:这一套原来叫
CustomToast/ToastManager/toastSuccess,现在全部收敛到PopupPrompt+PopupPromptManager两个对象上。旧名字在组件库里已经不存在,见到就是旧代码。 - 连续
show完全相同的内容(message / type / duration 三项全等)时状态不变,LaunchedEffect不会重启 —— 也就是提示还在显示时再发一条一模一样的,不会重新计时。需要强制重建就传个不同的duration。