前言
但凡用过鸿蒙原生弹窗的小伙伴,就能体会到它们是有多么的难用和奇葩,什么AlertDialog,CustomDialog,SubWindow,bindXxx,只要大家用心去体验,就能发现他们有很多离谱的设计和限制,时常就是一边用,一边骂骂咧咧的吐槽
实属无奈,就把鸿蒙版的SmartDialog写出来了
flutter的自带的dialog是可以应对日常场景,例如:简单的打开一个弹窗,非UI模块使用,跨页面交互之类;flutter_smart_dialog 是补齐了大多数的业务场景和一些强大的特殊能力,flutter_smart_dialog 对于flutter而言,日常场景是锦上添花,特殊场景是雪中送炭
但是 ohos_smart_dialog 对于鸿蒙而言,日常场景就是雪中送炭!单单一个使用方式而言,就是吊打鸿蒙的CustomDialog,CustomDialog的各种限制和使用方式,我不想再去提及和吐槽了
有时候,简洁的使用,才是最大的魅力
鸿蒙版的SmartDialog有什么优势?
- 单次初始化后即可使用,无需多处配置相关Component
- 优雅,极简的用法
- 非UI区域内使用,自定义Component
- 返回事件处理,优化的跨页面交互
- 多弹窗能力,多位置弹窗:上下左右中间
- 定位弹窗:自动定位目标Component
- 极简用法的loading弹窗
- 等等......
目前 flutter_smart_dialog 的代码量16w+,完整复刻其功能,工作量非常大,目前只能逐步实现一些基础能力,由于鸿蒙api的设计和相关限制,用法和相关初始化都有一定程度的妥协
鸿蒙版本的SmartDialog,功能会逐步和 flutter_smart_dialog 对齐(长期),api会尽量保持一致
效果
- Tablet 模拟器目前有些问题,会导致动画闪烁,请忽略;注:真机动画丝滑流畅,无任何问题
极简用法
// dialog
SmartDialog.
show({
builder: dialogArgs,
builderArgs:
Math.
random(),
})
@Builder
function
dialogArgs(
args: number) {
Text(args.
toString()).
padding(
50).
backgroundColor(
Color.
White)
}
// loading
SmartDialog.
showLoading()
安装
- github:https://github.com/xdd666t/ohos_smart_dialog
- ohos:https://ohpm.openharmony.cn/#/cn/detail/ohos_smart_dialog
ohpm install ohos_smart_dialog
配置
下述的配置项,可能会有一点多,但,这也是为了极致的体验;同时也是无奈之举,相关配置难以在内部去闭环处理,只能在外部去配置
这些配置,只需要配置一次,后续无需关心
完成下述的配置后,你将可以在任何地方使用弹窗,没有任何限制
初始化
- 因为弹窗需要处理跨页面交互,必须要监控路由
@Entry
@Component
struct
Index {
navPathStack:
NavPathStack =
new
NavPathStack()
build(
) {
Stack() {
// here: monitor router
Navigation(
OhosSmartDialog.
registerRouter(
this.
navPathStack)) {
MainPage()
}
.
mode(
NavigationMode.
Stack)
.
hideTitleBar(
true)
.
navDestination(pageMap)
// here
OhosSmartDialog()
}.
height(
'100%').
width(
'100%')
}
}
返回事件监听
别问我为啥返回事件的监听,处理的这么不优雅,鸿蒙里面没找全局返回事件监听,我也没辙。。。
- 如果你无需处理返回事件,可以使用下述写法
// Entry页面处理
@Entry
@Component
struct
Index {
onBackPress():
boolean |
void {
return
OhosSmartDialog.
onBackPressed()()
}
}
// 路由子页面
struct
JumpPage {
build(
) {
NavDestination() {
// ....
}
.
onBackPressed(
OhosSmartDialog.
onBackPressed())
}
}
- 如果你需要处理返回事件,在OhosSmartDialog.onBackPressed()中传入你的方法即可
// Entry页面处理
@Entry
@Component
struct
Index {
onBackPress():
boolean |
void {
return
OhosSmartDialog.
onBackPressed(
this.
onCustomBackPress)()
}
onCustomBackPress():
boolean {
return
false
}
}
// 路由子页面
@Component
struct
JumpPage {
build(
) {
NavDestination() {
// ...
}
.
onBackPressed(
OhosSmartDialog.
onBackPressed(
this.
onCustomBackPress))
}
onCustomBackPress():
boolean {
return
false
}
}
路由监听
- 一般来说,你无需关注SmartDialog的路由监听,因为内部已经设置了路由监听拦截器
- 但是,NavPathStack仅支持单拦截器(setInterception),如果业务代码也使用了这个api,会导致SmartDialog的路由监听被覆盖,从而失效
如果出现该情况,请参照下述解决方案
- 在你的路由监听类中手动调用
OhosSmartDialog.observe
export
default
class
YourNavigatorObserver
implements
NavigationInterception {
willShow?:
InterceptionShowCallback =
(from, to, operation, isAnimated) => {
OhosSmartDialog.
observe.
willShow?.(
from, to, operation, isAnimated)
// ...
}
didShow?:
InterceptionShowCallback =
(from, to, operation, isAnimated) => {
OhosSmartDialog.
observe.
didShow?.(
from, to, operation, isAnimated)
// ...
}
}
适配暗黑模式
- 为了极致的体验,深色模式切换时,打开态弹窗也应刷新为对应模式的样式,故需要进行下述配置
export
default
class
EntryAbility
extends
UIAbility {
onConfigurationUpdate(
newConfig:
Configuration):
void {
OhosSmartDialog.
onConfigurationUpdate(newConfig)
}
}
SmartConfig
- 支持全局配置弹窗的默认属性
function
init(
) {
// show
SmartDialog.
config.
custom.
maskColor =
"#75000000"
SmartDialog.
config.
custom.
alignment =
Alignment.
Center
// showAttach
SmartDialog.
config.
attach.
attachAlignmentType =
SmartAttachAlignmentType.
center
}
- 检查弹窗是否存在
// 检查当前是否有CustomDialog,AttachDialog或LoadingDialog处于打开状态
let isExist =
SmartDialog.
checkExist()
// 检查当前是否有AttachDialog处于打开状态
let isExist =
SmartDialog.
checkExist({
dialogTypes: [
SmartAllDialogType.
attach] })
// 检查当前是否有tag为“xxx”的dialog处于打开状态
let isExist =
SmartDialog.
checkExist({
tag:
"xxx" })
配置全局默认样式
- ShowLoading 自定样式十分简单
SmartDialog.
showLoading({
builder: customLoading })
但是对于大家来说,肯定是想用 SmartDialog.showLoading()
这种简单写法,所以支持自定义全局默认样式
- 需要在 OhosSmartDialog 上配置自定义的全局默认样式
@Entry
@Component
struct
Index {
build(
) {
Stack() {
OhosSmartDialog({
// custom global loading
loadingBuilder: customLoading,
})
}.
height(
'100%').
width(
'100%')
}
}
@Builder
export
function
customLoading(
args: ESObject) {
LoadingProgress().
width(
80).
height(
80).
color(
Color.
White)
}
- 配置完你的自定样式后,使用下述代码,就会显示你的 loading 样式
SmartDialog.
showLoading()
// 支持入参,可以在特殊场景下灵活配置
SSmartDialog.
showLoading({
builderArgs:
1 })
CustomDialog
- 下方会共用的方法
export
function
randomColor(
):
string {
const
letters:
string =
'0123456789ABCDEF';
let color =
'#';
for (
let i =
0; i <
6; i++) {
color += letters[
Math.
floor(
Math.
random() *
16)];
}
return color;
}
export
function
delay(
ms?: number):
Promise<
void> {
return
new
Promise(
resolve =>
setTimeout(resolve, ms));
}
传参弹窗
export
function
customUseArgs(
) {
SmartDialog.
show({
builder: dialogArgs,
// 支持任何类型
builderArgs:
Math.
random(),
})
}
@Builder
function
dialogArgs(
args: number) {
Text(
`${args}`).
fontColor(
Color.
White).
padding(
50)
.
borderRadius(
12).
backgroundColor(
randomColor())
}
多位置弹窗
export
async
function
customLocation(
) {
const animationTime =
1000
SmartDialog.
show({
builder: dialogLocationHorizontal,
alignment:
Alignment.
Start,
})
await
delay(animationTime)
SmartDialog.
show({
builder: dialogLocationVertical,
alignment:
Alignment.
Top,
})
}
@Builder
function
dialogLocationVertical(
) {
Text(
"location")
.
width(
"100%")
.
height(
"20%")
.
fontSize(
20)
.
fontColor(
Color.
White)
.
textAlign(
TextAlign.
Center)
.
padding(
50)
.
backgroundColor(
randomColor())
}
@Builder
function
dialogLocationHorizontal(
) {
Text(
"location")
.
width(
"30%")
.
height(
"100%")
.
fontSize(
20)
.
fontColor(
Color.
White)
.
textAlign(
TextAlign.
Center)
.
padding(
50)
.
backgroundColor(
randomColor())
}
跨页面交互
- 正常使用,无需设置什么参数
export
function
customJumpPage(
) {
SmartDialog.
show({
builder: dialogJumpPage,
})
}
@Builder
function
dialogJumpPage(
) {
Text(
"JumPage")
.
fontSize(
30)
.
padding(
50)
.
borderRadius(
12)
.
fontColor(
Color.
White)
.
backgroundColor(
randomColor())
.
onClick(
() => {
// 跳转页面
})
}
关闭指定弹窗
export
async
function
customTag(
) {
const animationTime =
1000
SmartDialog.
show({
builder: dialogTagA,
alignment:
Alignment.
Start,
tag:
"A",
})
await
delay(animationTime)
SmartDialog.
show({
builder: dialogTagB,
alignment:
Alignment.
Top,
tag:
"B",
})
}
@Builder
function
dialogTagA(
) {
Text(
"A")
.
width(
"20%")
.
height(
"100%")
.
fontSize(
20)
.
fontColor(
Color.
White)
.
textAlign(
TextAlign.
Center)
.
padding(
50)
.
backgroundColor(
randomColor())
}
@Builder
function
dialogTagB(
) {
Flex({
wrap:
FlexWrap.
Wrap }) {
ForEach([
"closA",
"closeSelf"],
(item: string, index: number) => {
Button(item)
.
backgroundColor(
"#4169E1")
.
margin(
10)
.
onClick(
() => {
if (index ===
0) {
SmartDialog.
dismiss({
tag:
"A" })
}
else
if (index ===
1) {
SmartDialog.
dismiss({
tag:
"B" })
}
})
})
}.
backgroundColor(
Color.
White).
width(
350).
margin({
left:
30,
right:
30 }).
padding(
10).
borderRadius(
10)
}
自定义遮罩
export
function
customMask(
) {
SmartDialog.
show({
builder: dialogShowDialog,
maskBuilder: dialogCustomMask,
})
}
@Builder
function
dialogCustomMask(
) {
Stack().
width(
"100%").
height(
"100%").
backgroundColor(
randomColor()).
opacity(
0.6)
}
@Builder
function
dialogShowDialog(
) {
Text(
"showDialog")
.
fontSize(
30)
.
padding(
50)
.
fontColor(
Color.
White)
.
borderRadius(
12)
.
backgroundColor(
randomColor())
.
onClick(
() =>
customMask())
}
AttachDialog
默认定位
export
function
attachEasy(
) {
SmartDialog.
show({
builder: dialog
})
}
@Builder
function
dialog(
) {
Stack() {
Text(
"Attach")
.
backgroundColor(
randomColor())
.
padding(
20)
.
fontColor(
Color.
White)
.
borderRadius(
5)
.
onClick(
() => {
SmartDialog.
showAttach({
targetId:
"Attach",
builder: targetLocationDialog,
})
})
.
id(
"Attach")
}
.
borderRadius(
12)
.
padding(
50)
.
backgroundColor(
Color.
White)
}
@Builder
function
targetLocationDialog(
) {
Text(
"targetIdDialog")
.
fontSize(
20)
.
fontColor(
Color.
White)
.
textAlign(
TextAlign.
Center)
.
padding(
50)
.
borderRadius(
12)
.
backgroundColor(
randomColor())
}
多方向定位
export
function
attachLocation(
) {
SmartDialog.
show({
builder: dialog
})
}
class
AttachLocation {
title:
string =
""
alignment?:
Alignment
}
const
locationList:
Array<
AttachLocation> = [
{
title:
"TopStart",
alignment:
Alignment.
TopStart },
{
title:
"Top",
alignment:
Alignment.
Top },
{
title:
"TopEnd",
alignment:
Alignment.
TopEnd },
{
title:
"Start",
alignment:
Alignment.
Start },
{
title:
"Center",
alignment:
Alignment.
Center },
{
title:
"End",
alignment:
Alignment.
End },
{
title:
"BottomStart",
alignment:
Alignment.
BottomStart },
{
title:
"Bottom",
alignment:
Alignment.
Bottom },
{
title:
"BottomEnd",
alignment:
Alignment.
BottomEnd },
]
@Builder
function
dialog(
) {
Column() {
Grid() {
ForEach(locationList,
(item: AttachLocation) => {
GridItem() {
buildButton(item.
title,
() => {
SmartDialog.
showAttach({
targetId: item.
title,
alignment: item.
alignment,
maskColor:
Color.
Transparent,
builder: targetLocationDialog
})
})
}
})
}.
columnsTemplate(
'1fr 1fr 1fr').
height(
220)
buildButton(
"allOpen",
async () => {
for (
let index =
0; index < locationList.
length; index++) {
let item = locationList[index]
SmartDialog.
showAttach({
targetId: item.
title,
alignment: item.
alignment,
maskColor:
Color.
Transparent,
builder: targetLocationDialog,
})
await
delay(
300)
}
},
randomColor())
}
.
borderRadius(
12)
.
width(
700)
.
padding(
30)
.
backgroundColor(
Color.
White)
}
@Builder
function
buildButton(
title: string, onClick?: VoidCallback, bgColor?: ResourceColor) {
Text(title)
.
backgroundColor(bgColor ??
"#4169E1")
.
constraintSize({
minWidth:
120,
minHeight:
46 })
.
margin(
10)
.
textAlign(
TextAlign.
Center)
.
fontColor(
Color.
White)
.
borderRadius(
5)
.
onClick(onClick)
.
id(title)
}
@Builder
function
targetLocationDialog(
) {
Text(
"targetIdDialog")
.
fontSize(
20)
.
fontColor(
Color.
White)
.
textAlign(
TextAlign.
Center)
.
padding(
50)
.
borderRadius(
12)
.
backgroundColor(
randomColor())
}
Loading
对于Loading而言,应该有几个比较明显的特性
- loading和dialog都存在页面上,哪怕dialog打开,loading都应该显示dialog之上
- loading应该具有单一特性,多次打开loading,页面也应该只存在一个loading
- 刷新特性,多次打开loading,后续打开的loading样式,应该覆盖之前打开的loading样式
- loading使用频率非常高,应该支持强大的拓展和极简的使用
从上面列举几个特性而言,loading是一个非常特殊的dialog,所以需要针对其特性,进行定制化的实现
当然了,内部已经屏蔽了细节,在使用上,和dialog的使用没什么区别
默认loading
SmartDialog.
showLoading()
自定义Loading
- 点击loading后,会再次打开一个loading,从效果图可以看出它的单一刷新特性
export
function
loadingCustom(
) {
SmartDialog.
showLoading({
builder: customLoading,
})
}
@Builder
export
function
customLoading(
) {
Column({
space:
5 }) {
Text(
"again open loading").
fontSize(
16).
fontColor(
Color.
White)
LoadingProgress().
width(
80).
height(
80).
color(
Color.
White)
}
.
padding(
20)
.
borderRadius(
12)
.
onClick(
() =>
loadingCustom())
.
backgroundColor(
randomColor())
}
最后
鸿蒙版的SmartDialog,相信会对开发鸿蒙的小伙伴们有一些帮助.
现在就业环境真是让人头皮发麻,现在的各种技术群里,看到好多人公司各种拖欠工资,各种失业半年的情况
淦,不知道还能写多长时间代码!
__EOF__

本文链接: https://www.cnblogs.com/xdd666/p/18353193.html
关于博主:评论和私信会在第一时间回复。或者 直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角 【推荐】一下。您的鼓励是博主的最大动力!