OpenHarmony CustomDialog 自定义弹窗实战开发
前文使用的样式固定,仅能配置标题、文本、按钮,无法满足头像选择、表单弹窗、确认弹窗、底部弹窗等个性化界面需求。OpenHarmony 提供自定义弹窗组件,基于 ArkUI 自由布局,支持自定义样式、交互、尺寸、动画,是项目中高频使用的进阶组件。本文讲解自定义弹窗定义、调用传参、弹窗样式、关闭逻辑、嵌套交互,搭配多个可直接运行的案例,并结合全局状态完成综合实战。ets// 1. 定义自定义弹窗//
·
一、前言
前文使用的 AlertDialog 样式固定,仅能配置标题、文本、按钮,无法满足头像选择、表单弹窗、确认弹窗、底部弹窗等个性化界面需求。OpenHarmony 提供 CustomDialog 自定义弹窗组件,基于 ArkUI 自由布局,支持自定义样式、交互、尺寸、动画,是项目中高频使用的进阶组件。
本文讲解自定义弹窗定义、调用传参、弹窗样式、关闭逻辑、嵌套交互,搭配多个可直接运行的案例,并结合全局状态完成综合实战。
二、核心基础说明
- 定义规则:使用
@CustomDialog装饰器声明自定义弹窗,区别于普通页面组件@Component。 - 弹窗参数:通过
@DialogParam接收外部传入参数、回调函数,实现父子通信。 - 关闭弹窗:组件内置
controller.close()方法,主动关闭弹窗。 - 弹窗控制器:
CustomDialogController用于创建、唤起、控制弹窗。
三、基础用法:简单提示弹窗
3.1 基础自定义弹窗(纯提示 + 双按钮)
ets
// 1. 定义自定义弹窗
@CustomDialog
struct SimpleDialog {
// 弹窗控制器,内置方法:close()
controller: CustomDialogController
// 接收外部回调
@DialogParam confirmCallback: () => void
@DialogParam cancelCallback: () => void
build() {
Column({ space: 20 }) {
// 弹窗标题
Text("操作提示")
.fontSize(20)
.fontWeight(FontWeight.Bold)
// 内容
Text("确定执行当前操作吗?").fontSize(16)
// 按钮区域
Row({ space: 30 }) {
Button("取消")
.width(100)
.backgroundColor("#999")
.onClick(() => {
this.controller.close() // 关闭弹窗
this.cancelCallback() // 执行取消回调
})
Button("确定")
.width(100)
.backgroundColor("#007DFF")
.onClick(() => {
this.controller.close()
this.confirmCallback() // 执行确定回调
})
}
}
.width(300)
.padding(20)
.backgroundColor(Color.White)
.borderRadius(12)
}
}
// 2. 页面入口,调用弹窗
@Entry
@Component
struct DialogIndex {
// 创建弹窗控制器
dialogController: CustomDialogController = new CustomDialogController({
builder: SimpleDialog({
confirmCallback: () => {
console.log("点击了确定按钮")
},
cancelCallback: () => {
console.log("点击了取消按钮")
}
}),
// 弹窗配置:点击遮罩是否关闭弹窗
autoCancel: true
})
build() {
Column() {
Button("打开自定义提示弹窗")
.width(220)
.onClick(() => {
this.dialogController.open() // 唤起弹窗
})
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
}
}
3.2 配置项说明
在 CustomDialogController 中可配置常用属性:
autoCancel:布尔值,点击弹窗外部遮罩层是否关闭弹窗,默认truealignment:弹窗对齐方式(居中、底部、顶部、左侧、右侧)offset:弹窗偏移距离maskColor:遮罩层颜色
四、进阶案例 1:带输入框的表单弹窗
适用于弹窗内输入内容、评论、备注、修改昵称等场景,实现弹窗内数据收集。
ets
@CustomDialog
struct InputDialog {
controller: CustomDialogController
// 回传给父页面的输入内容
@DialogParam submit: (content: string) => void
@State inputText: string = ""
build() {
Column({ space: 18 }) {
Text("请输入备注信息").fontSize(18).fontWeight(FontWeight.Bold)
// 弹窗内输入框
TextInput({ placeholder: "在此输入内容", text: this.inputText })
.width("100%")
.height(45)
.onChange(val => this.inputText = val)
Row({ space: 20 }) {
Button("关闭")
.layoutWeight(1)
.backgroundColor("#EEEEEE")
.fontColor("#333")
.onClick(() => this.controller.close())
Button("提交")
.layoutWeight(1)
.backgroundColor("#007DFF")
.onClick(() => {
this.submit(this.inputText)
this.controller.close()
})
}
}
.width(320)
.padding(20)
.backgroundColor(Color.White)
.borderRadius(10)
}
}
@Entry
@Component
struct InputDialogPage {
@State result: string = ""
inputDialog: CustomDialogController = new CustomDialogController({
builder: InputDialog({
submit: (val: string) => {
this.result = "弹窗输入内容:" + val
}
}),
autoCancel: false // 禁止点击遮罩关闭
})
build() {
Column({ space: 20 }) {
Button("打开输入弹窗")
.width(220)
.onClick(() => this.inputDialog.open())
Text(this.result).fontSize(18)
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
.padding(20)
}
}
五、进阶案例 2:底部弹窗(底部弹出菜单)
通过 alignment 实现从页面底部弹出的菜单弹窗,常用于分享、操作菜单。
ets
@CustomDialog
struct BottomMenuDialog {
controller: CustomDialogController
@DialogParam menuClick: (index: number) => void
build() {
Column() {
List({ space: 1 }) {
ListItem() {
Text("分享好友")
.width("100%")
.height(50)
.textAlign(TextAlign.Center)
.fontSize(17)
.onClick(() => {
this.menuClick(1)
this.controller.close()
})
}
ListItem() {
Text("收藏内容")
.width("100%")
.height(50)
.textAlign(TextAlign.Center)
.fontSize(17)
.onClick(() => {
this.menuClick(2)
this.controller.close()
})
}
ListItem() {
Text("举报")
.width("100%")
.height(50)
.textAlign(TextAlign.Center)
.fontSize(17)
.onClick(() => {
this.menuClick(3)
this.controller.close()
})
}
}
.width("100%")
// 取消按钮
Button("取消")
.width("100%")
.height(50)
.margin({ top: 10 })
.backgroundColor(Color.White)
.onClick(() => this.controller.close())
}
.width("100%")
.backgroundColor("#F5F5F5")
}
}
@Entry
@Component
struct BottomDialogPage {
@State tip: string = ""
bottomDialog: CustomDialogController = new CustomDialogController({
builder: BottomMenuDialog({
menuClick: (idx: number) => {
switch (idx) {
case 1:
this.tip = "已选择:分享好友"
break
case 2:
this.tip = "已选择:收藏内容"
break
case 3:
this.tip = "已选择:举报"
break
}
}
}),
alignment: DialogAlignment.Bottom, // 弹窗底部对齐
autoCancel: true
})
build() {
Column({ space: 20 }) {
Button("打开底部菜单弹窗")
.width(220)
.onClick(() => this.bottomDialog.open())
Text(this.tip).fontSize(18)
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
}
}
六、综合实战:弹窗修改全局用户名(联动 AppStorage)
结合之前的全局状态 AppStorage,实现弹窗修改用户名 → 全局数据同步刷新完整业务。
ets
// 自定义修改昵称弹窗
@CustomDialog
struct EditNameDialog {
controller: CustomDialogController
// 接收父页面传入的原始名称
@DialogParam oldName: string
@DialogParam saveAction: (newName: string) => void
@State tempName: string = ""
aboutToAppear() {
// 弹窗打开时赋值初始值
this.tempName = this.oldName
}
build() {
Column({ space: 15 }) {
Text("修改用户名").fontSize(20).fontWeight(FontWeight.Bold)
TextInput({ text: this.tempName })
.width("100%")
.height(45)
.onChange(val => this.tempName = val)
Row({ space: 20 }) {
Button("取消")
.layoutWeight(1)
.onClick(() => this.controller.close())
Button("保存")
.layoutWeight(1)
.backgroundColor("#007DFF")
.onClick(() => {
this.saveAction(this.tempName)
this.controller.close()
})
}
}
.width(300)
.padding(20)
.backgroundColor(Color.White)
.borderRadius(12)
}
}
@Entry
@Component
struct UserInfoPage {
// 双向绑定全局用户名
@StorageLink("userName") userName: string
editDialog: CustomDialogController = new CustomDialogController({
builder: EditNameDialog({
oldName: this.userName,
saveAction: (newName: string) => {
// 修改全局数据,所有绑定页面自动刷新
this.userName = newName
}
})
})
build() {
Column({ space: 25 }) {
Text("当前用户名:" + this.userName).fontSize(20)
Button("点击修改用户名")
.width(220)
.onClick(() => this.editDialog.open())
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
.padding(20)
}
}
七、开发要点、踩坑与最佳实践
7.1 核心使用规则
- 装饰器区分:
@CustomDialog:仅用于定义弹窗,不能作为独立页面@Component:普通组件 / 页面
- 弹窗内部必须依赖
controller.close()关闭,无法直接路由关闭。 - 外部传参、回调统一使用
@DialogParam,不建议直接使用@Prop。
7.2 常用配置技巧
- 居中弹窗(默认):无需设置
alignment - 底部弹窗:
alignment: DialogAlignment.Bottom - 禁止点击遮罩关闭:
autoCancel: false(表单、重要确认弹窗推荐开启) - 自定义遮罩:
maskColor: "rgba(0,0,0,0.6)"
7.3 常见问题解决
- 弹窗多次创建报错 控制器建议定义在组件顶层,不要在
onClick内部重复new。 - 弹窗输入框数据不回显 在弹窗
aboutToAppear生命周期中接收初始值,不要直接赋值。 - 弹窗尺寸适配 固定宽高适配小弹窗,百分比宽高适配底部全屏菜单。
7.4 项目规范
- 按业务拆分弹窗:提示弹窗、表单弹窗、菜单弹窗,单独文件管理。
- 重要操作弹窗(退出账号、删除数据)设置
autoCancel: false,防止误触。 - 弹窗样式统一:圆角、内边距、配色保持项目 UI 风格一致。
更多推荐


所有评论(0)