HarmonyOS鸿蒙Next中如何封装通用的Dialog组件?
HarmonyOS鸿蒙Next中如何封装通用的Dialog组件?
应用中需要频繁使用确认对话框、选择器对话框等,如何封装通用的Dialog组件避免重复代码?
如何实现优雅的回调处理?
3 回复
实现代码
/**
* 确认对话框
*/
[@CustomDialog](/user/CustomDialog)
export struct ConfirmDialog {
controller: CustomDialogController;
title: string = '提示';
message: string = '';
confirmText: string = '确定';
cancelText: string = '取消';
onConfirm?: () => void;
onCancel?: () => void;
build() {
Column({ space: 16 }) {
// 标题
Text(this.title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width('100%')
// 消息内容
Text(this.message)
.fontSize(14)
.fontColor('#666')
.width('100%')
.margin({ top: 8, bottom: 16 })
// 按钮组
Row({ space: 12 }) {
Button(this.cancelText)
.fontSize(16)
.backgroundColor('#f5f5f5')
.fontColor('#333')
.layoutWeight(1)
.onClick(() => {
this.controller.close();
if (this.onCancel) {
this.onCancel();
}
})
Button(this.confirmText)
.fontSize(16)
.backgroundColor('#ff6b6b')
.fontColor('#fff')
.layoutWeight(1)
.onClick(() => {
this.controller.close();
if (this.onConfirm) {
this.onConfirm();
}
})
}
.width('100%')
}
.padding(20)
.backgroundColor(Color.White)
.borderRadius(12)
}
}
/**
* 单选对话框
*/
[@CustomDialog](/user/CustomDialog)
export struct SelectDialog {
controller: CustomDialogController;
title: string = '请选择';
options: string[] = [];
selectedIndex: number = 0;
onSelect?: (index: number, value: string) => void;
[@State](/user/State) currentIndex: number = 0;
aboutToAppear() {
this.currentIndex = this.selectedIndex;
}
build() {
Column({ space: 12 }) {
// 标题
Text(this.title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width('100%')
.padding({ bottom: 12 })
// 选项列表
List({ space: 0 }) {
ForEach(this.options, (option: string, index: number) => {
ListItem() {
Row() {
Text(option)
.fontSize(16)
.fontColor(this.currentIndex === index ? '#ff6b6b' : '#333')
.layoutWeight(1)
if (this.currentIndex === index) {
Text('✓')
.fontSize(18)
.fontColor('#ff6b6b')
}
}
.width('100%')
.padding(12)
.backgroundColor(this.currentIndex === index ? '#fff5f5' : Color.White)
.borderRadius(8)
.onClick(() => {
this.currentIndex = index;
})
}
})
}
.height(Math.min(this.options.length * 48, 300))
// 确定按钮
Button('确定')
.width('100%')
.backgroundColor('#ff6b6b')
.fontColor('#fff')
.margin({ top: 12 })
.onClick(() => {
this.controller.close();
if (this.onSelect) {
this.onSelect(this.currentIndex, this.options[this.currentIndex]);
}
})
}
.padding(20)
.backgroundColor(Color.White)
.borderRadius(12)
.width('80%')
}
}
/**
* 输入对话框
*/
[@CustomDialog](/user/CustomDialog)
export struct InputDialog {
controller: CustomDialogController;
title: string = '输入';
placeholder: string = '请输入';
defaultValue: string = '';
inputType: InputType = InputType.Normal;
maxLength: number = 50;
onConfirm?: (value: string) => void;
[@State](/user/State) inputValue: string = '';
aboutToAppear() {
this.inputValue = this.defaultValue;
}
build() {
Column({ space: 16 }) {
// 标题
Text(this.title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.width('100%')
// 输入框
TextInput({ text: this.inputValue, placeholder: this.placeholder })
.type(this.inputType)
.maxLength(this.maxLength)
.onChange((value: string) => {
this.inputValue = value;
})
.width('100%')
.padding(12)
.borderRadius(8)
.backgroundColor('#f5f5f5')
// 按钮组
Row({ space: 12 }) {
Button('取消')
.fontSize(16)
.backgroundColor('#f5f5f5')
.fontColor('#333')
.layoutWeight(1)
.onClick(() => {
this.controller.close();
})
Button('确定')
.fontSize(16)
.backgroundColor('#ff6b6b')
.fontColor('#fff')
.layoutWeight(1)
.onClick(() => {
this.controller.close();
if (this.onConfirm) {
this.onConfirm(this.inputValue);
}
})
}
.width('100%')
}
.padding(20)
.backgroundColor(Color.White)
.borderRadius(12)
}
}
/**
* 加载对话框
*/
[@CustomDialog](/user/CustomDialog)
export struct LoadingDialog {
controller: CustomDialogController;
message: string = '加载中...';
build() {
Column({ space: 16 }) {
LoadingProgress()
.width(50)
.height(50)
.color('#ff6b6b')
Text(this.message)
.fontSize(14)
.fontColor('#666')
}
.padding(30)
.backgroundColor(Color.White)
.borderRadius(12)
}
}
使用示例
@Entry
@Component
struct DemoPage {
private confirmDialogController: CustomDialogController | null = null;
private selectDialogController: CustomDialogController | null = null;
private inputDialogController: CustomDialogController | null = null;
private loadingDialogController: CustomDialogController | null = null;
// 显示确认对话框
showConfirmDialog() {
this.confirmDialogController = new CustomDialogController({
builder: ConfirmDialog({
title: '删除确认',
message: '确定要删除这条记录吗?此操作不可恢复。',
confirmText: '删除',
cancelText: '取消',
onConfirm: () => {
console.log('用户点击了删除');
this.deleteRecord();
},
onCancel: () => {
console.log('用户取消了删除');
}
}),
autoCancel: true,
alignment: DialogAlignment.Center
});
this.confirmDialogController.open();
}
// 显示选择对话框
showSelectDialog() {
this.selectDialogController = new CustomDialogController({
builder: SelectDialog({
title: '选择关系',
options: ['朋友', '同事', '亲戚', '同学', '其他'],
selectedIndex: 0,
onSelect: (index: number, value: string) => {
console.log(`选择了: ${value}`);
}
}),
autoCancel: true,
alignment: DialogAlignment.Center
});
this.selectDialogController.open();
}
// 显示输入对话框
showInputDialog() {
this.inputDialogController = new CustomDialogController({
builder: InputDialog({
title: '添加备注',
placeholder: '请输入备注内容',
defaultValue: '',
maxLength: 100,
onConfirm: (value: string) => {
console.log(`输入内容: ${value}`);
}
}),
autoCancel: true,
alignment: DialogAlignment.Center
});
this.inputDialogController.open();
}
// 显示加载对话框
async showLoadingDialog() {
this.loadingDialogController = new CustomDialogController({
builder: LoadingDialog({
message: '正在保存...'
}),
autoCancel: false,
alignment: DialogAlignment.Center
});
this.loadingDialogController.open();
// 模拟异步操作
await this.saveData();
// 关闭加载对话框
this.loadingDialogController.close();
}
async deleteRecord() {
// 删除逻辑
}
async saveData() {
// 保存逻辑
}
build() {
Column({ space: 16 }) {
Button('确认对话框').onClick(() => this.showConfirmDialog())
Button('选择对话框').onClick(() => this.showSelectDialog())
Button('输入对话框').onClick(() => this.showInputDialog())
Button('加载对话框').onClick(() => this.showLoadingDialog())
}
.padding(20)
}
}
原理解析
1. @CustomDialog装饰器
[@CustomDialog](/user/CustomDialog)
export struct ConfirmDialog {
controller: CustomDialogController;
}
- 标记为自定义对话框组件
- 必须包含controller属性
- 通过controller控制显示/隐藏
2. 回调函数传递
onConfirm?: () => void;
- 使用可选属性定义回调
- 调用前检查是否存在
- 支持传递参数
3. @State状态管理
[@State](/user/State) currentIndex: number = 0;
- 对话框内部状态
- 响应用户交互
- 触发UI更新
最佳实践
- 统一风格: 所有对话框使用相同的样式和动画
- 回调处理: 使用可选回调,调用前检查
- 自动关闭: 设置autoCancel: true支持点击外部关闭
- 内存管理: 对话框关闭后controller置null
- 异步操作: 加载对话框配合async/await使用
避坑指南
- 忘记close: 必须手动调用controller.close()
- 重复打开: 打开前检查controller是否已存在
- 内存泄漏: 组件销毁时关闭所有对话框
- 回调丢失: 箭头函数保持this指向
- 样式覆盖: 使用width/height限制对话框大小
效果展示
- 确认对话框:标题+消息+双按钮
- 选择对话框:标题+列表+确定按钮
- 输入对话框:标题+输入框+双按钮
- 加载对话框:加载动画+提示文字
更多关于HarmonyOS鸿蒙Next中如何封装通用的Dialog组件?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中封装通用Dialog组件,可使用ArkTS的@CustomDialog装饰器。通过@CustomDialog定义自定义弹窗组件,结合@Prop、@Link或@State装饰器管理弹窗状态与数据传递。封装时需设计统一的样式布局、交互事件回调接口,并利用组件复用机制实现通用性。可封装基础弹窗模板,通过参数控制标题、内容、按钮等元素的显示与行为。
在HarmonyOS Next中封装通用Dialog组件,推荐使用ArkUI的CustomDialogController和自定义组件实现。以下是核心方案:
- 基础封装结构
// CommonDialog.ets
@Component
export struct CommonDialog {
[@Prop](/user/Prop) title: ResourceStr = ''
[@Prop](/user/Prop) content: ResourceStr = ''
[@Prop](/user/Prop) confirmText: ResourceStr = $r('app.string.confirm')
[@Prop](/user/Prop) cancelText: ResourceStr = $r('app.string.cancel')
[@Link](/user/Link) isShow: boolean
private controller: CustomDialogController
aboutToAppear() {
this.controller = new CustomDialogController({
builder: this,
cancel: () => { this.isShow = false },
autoCancel: true
})
}
build() {
Column({ space: 12 }) {
Text(this.title).fontSize(18)
Text(this.content).fontSize(14)
Flex({ justifyContent: FlexAlign.SpaceAround }) {
Button(this.cancelText)
.onClick(() => {
this.controller.close()
this.onCancel?.()
})
Button(this.confirmText)
.onClick(() => {
this.controller.close()
this.onConfirm?.()
})
}
}.padding(20)
}
// 回调函数
private onConfirm?: () => void
private onCancel?: () => void
// 设置回调的链式方法
setOnConfirm(callback: () => void): CommonDialog {
this.onConfirm = callback
return this
}
setOnCancel(callback: () => void): CommonDialog {
this.onCancel = callback
return this
}
}
- 使用示例
// 在页面中使用
@State dialogVisible: boolean = false
build() {
Column() {
Button('显示对话框')
.onClick(() => {
this.dialogVisible = true
})
CommonDialog({
title: '确认操作',
content: '确定要执行此操作吗?',
isShow: $dialogVisible
})
.setOnConfirm(() => {
// 确认回调逻辑
console.log('用户确认')
})
.setOnCancel(() => {
// 取消回调逻辑
console.log('用户取消')
})
}
}
- 扩展为选择器对话框
// SelectorDialog.ets
@Component
export struct SelectorDialog {
[@Prop](/user/Prop) options: string[] = []
[@Link](/user/Link) selectedIndex: number
[@Link](/user/Link) isShow: boolean
private controller: CustomDialogController
build() {
Column() {
List({ space: 10 }) {
ForEach(this.options, (item: string, index: number) => {
ListItem() {
Text(item)
.onClick(() => {
this.selectedIndex = index
this.controller.close()
this.onSelect?.(index, item)
})
}
})
}
}
}
private onSelect?: (index: number, value: string) => void
setOnSelect(callback: (index: number, value: string) => void): SelectorDialog {
this.onSelect = callback
return this
}
}
- 回调处理优化
- 使用Promise包装异步操作:
async showConfirmDialog(): Promise<boolean> {
return new Promise((resolve) => {
CommonDialog({
title: '提示',
content: '确认删除?',
isShow: $dialogVisible
})
.setOnConfirm(() => resolve(true))
.setOnCancel(() => resolve(false))
})
}
// 使用
const result = await this.showConfirmDialog()
- 配置化管理 创建DialogConfig统一管理样式和文案:
export class DialogConfig {
static readonly CONFIRM = {
title: $r('app.string.confirm_title'),
confirmText: $r('app.string.ok'),
cancelText: $r('app.string.cancel')
}
static readonly WARNING = {
title: $r('app.string.warning_title'),
confirmColor: Color.Red
}
}
关键点:
这种封装方式实现了对话框的复用,回调处理清晰,且易于扩展新类型的对话框。

