CMP for OpenHarmony:AlertDialog 对话框的“语义边界”实现——提示/确认/不可取消/pending 写回四种模式一次讲清(项目实战代码)

代码地址:通过网盘分享的文件:cmp_openharmony.zip
链接: https://pan.baidu.com/s/15rN1LvJ0KENMkYZfLq_R1Q?pwd=nhqe 提取码: nhqe

1. 代码位置:示例页在哪

本文所有 Kotlin 代码均来自下面文件(复制路径即可定位):

composeApp/src/commonMain/kotlin/com/tencent/compose/sample/dialog/DialogDemoPage.kt


2. 先把“弹窗类型”拆成 4 个可组合状态

示例页用 4 个 Boolean 控制不同对话框的显示(节选,保持项目原样):

var showInfo by remember { mutableStateOf(false) }
var showConfirm by remember { mutableStateOf(false) }
var showNonCancelable by remember { mutableStateOf(false) }
var showPending by remember { mutableStateOf(false) }

说明:

  • Demo 同时演示多种对话框,因此用多个开关分别控制。
  • 工程中如果只会出现“同一时刻最多一个弹窗”,也可以把它们合并成一个枚举状态,但本示例用多开关更直观。

3. 给弹窗一个“可观测输出”:lastResult 用于记录触发路径

示例页定义了 lastResult 作为结果记录(节选,保持项目原样):

var lastResult by remember { mutableStateOf("未触发") }

说明:

  • Demo 页面里它负责把“关闭/确认/取消/拦截 dismiss”等行为可视化。
  • 工程里你可以把 lastResult 替换为日志、埋点或状态栏提示;关键是让对话框行为可追踪。

4. 提示型 AlertDialog:单按钮关闭,dismiss 与 confirm 都要显式处理

提示型对话框通常用于“告知”,它只有一个确认按钮,但仍然要处理两条关闭路径:

  • 点击空白区域/返回:触发 onDismissRequest
  • 点击确认按钮:触发 confirmButton

示例实现(节选,保持项目原样):

if (showInfo) {
    AlertDialog(
        onDismissRequest = {
            showInfo = false
            lastResult = "提示型:关闭"
        },
        title = { Text(text = "提示") },
        text = {
            Text(
                text = "这是一个最基础的提示型对话框:通常只提供一个确认按钮,用于告知用户信息。",
                fontSize = 13.sp,
                color = Color(0xFF333333)
            )
        },
        confirmButton = {
            Button(
                onClick = {
                    showInfo = false
                    lastResult = "提示型:确认"
                }
            ) {
                Text(text = "知道了", color = Color.White)
            }
        }
    )
}

说明:

  • 这里的关键不是 UI 文案,而是两条关闭路径都要落到同一套状态变更
    • 都会把 showInfo = false,确保弹窗从树上移除。
  • onDismissRequest 不应该留空,否则你会遇到“用户点击空白区域没反应”的不一致体验。

5. 确认/取消 AlertDialog:确认有副作用,取消必须保证“不写入”

确认/取消对话框的核心语义是:

  • 确认:执行副作用(写入主状态、调用接口、提交配置等)
  • 取消/关闭:不执行副作用

示例实现(节选,保持项目原样):

if (showConfirm) {
    AlertDialog(
        onDismissRequest = {
            showConfirm = false
            lastResult = "确认/取消:关闭"
        },
        title = { Text(text = "确认操作") },
        text = {
            Text(
                text = "确认后会把 count + 10,并且会把 switch 置为 ON;取消则不修改主状态。",
                fontSize = 13.sp,
                color = Color(0xFF333333)
            )
        },
        confirmButton = {
            Button(
                onClick = {
                    count += 10
                    switchValue = true
                    showConfirm = false
                    lastResult = "确认/取消:已确认(count=$count, switch=ON)"
                }
            ) {
                Text(text = "确认", color = Color.White)
            }
        },
        dismissButton = {
            Button(
                onClick = {
                    showConfirm = false
                    lastResult = "确认/取消:取消"
                },
                colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFFE0E0E0))
            ) {
                Text(text = "取消", color = Color(0xFF333333))
            }
        }
    )
}

说明:

  • 副作用只放在 confirmButton:本例确认时写入 count/switchValue
  • onDismissRequestdismissButton 都不写入主状态,只负责关闭弹窗并记录来源。
  • 这能避免一个常见 bug:用户只是误触关闭,却触发了状态写入。

6. 不可取消 AlertDialog:用 onDismissRequest 显式拦截

有些业务场景不允许“点空白区域就关闭”(例如必须确认协议、必须等待某个关键步骤)。这种情况下要把 onDismissRequest 变成一个“拦截器”。

示例实现(节选,保持项目原样):

if (showNonCancelable) {
    AlertDialog(
        onDismissRequest = {
            lastResult = "不可取消:已拦截 dismiss"
        },
        title = { Text(text = "不可取消") },
        text = {
            Text(
                text = "这个对话框不允许点击空白区域关闭,你必须点击下方按钮主动关闭。",
                fontSize = 13.sp,
                color = Color(0xFF333333)
            )
        },
        confirmButton = {
            Button(
                onClick = {
                    showNonCancelable = false
                    lastResult = "不可取消:关闭"
                }
            ) { Text(text = "关闭", color = Color.White) }
        }
    )
}

说明:

  • 这里 onDismissRequest 不把 showNonCancelable 置为 false,等价于“拦截”。
  • 真正的关闭只能从 confirmButton 走,这样对话框的关闭路径是可控的。

7. pending 写回:把对话框当作“编辑缓冲区”,确认时再落主状态

很多对话框并不是单纯确认/取消,而是“在弹窗里编辑多个字段”。如果你在弹窗里每点一下就立刻写回主状态,会出现两类问题:

  • 用户还没点确认,主界面状态已经变了(语义不一致)
  • 用户点取消时很难恢复(需要回滚逻辑)

示例页采用 pendingSwitch/pendingCount 作为缓冲区,并在打开弹窗时初始化(节选,保持项目原样):

var pendingSwitch by remember { mutableStateOf(switchValue) }
var pendingCount by remember { mutableIntStateOf(count) }

LaunchedEffect(showPending) {
    if (showPending) {
        pendingSwitch = switchValue
        pendingCount = count
    }
}

说明:

  • pending* 是弹窗内部的临时值。
  • LaunchedEffect(showPending) 确保每次打开弹窗都从主状态同步一份初始值,避免上次编辑残留。

7.1 在弹窗内部修改 pending 值

示例里通过 Switch 和两个可点击区域修改 pendingCount(节选,保持项目原样):

Switch(checked = pendingSwitch, onCheckedChange = { pendingSwitch = it })

pendingCount -= 1

pendingCount += 1

说明:

  • 这里的重点是:所有修改都只作用于 pending*,不会影响页面主状态。
  • 这让“取消”变得廉价:直接关闭弹窗即可,不需要做回滚。

7.2 点击“写回”才更新主状态

写回按钮实现(节选,保持项目原样):

Button(
    onClick = {
        switchValue = pendingSwitch
        count = pendingCount
        showPending = false
        lastResult = "pending:已写回(switch=${if (switchValue) "ON" else "OFF"}, count=$count)"
    }
) {
    Text(text = "写回", color = Color.White)
}

说明:

  • 这就是对话框的“提交边界”:只有在确认按钮点击时才写入主状态。
  • 这类模式非常适合做“设置项编辑”“批量参数调整”等场景。

8. 自检清单:AlertDialog 工程里最容易踩的坑

  • 关闭路径不一致confirmButton 关了弹窗,但 onDismissRequest 没关,会导致行为割裂。
  • 取消也产生副作用:确认/取消对话框里,副作用必须只在 confirm 分支。
  • 不可取消实现错误:想不可取消却还在 onDismissRequestshow=false,等于没有拦截。
  • 编辑型弹窗缺少 pending 缓冲区:导致“没点确认主状态就变了”,取消要做回滚。

在这里插入图片描述


欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net openharmony社区链接

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐