背景
??在項目開發(fā)中經(jīng)常會碰到有時要根據(jù)業(yè)務(wù)后臺接口顯示一些提示框或者引導框什么的,有時這些彈框要多次顯示或只顯示一次,并且它們內(nèi)部也有一定的優(yōu)先級,還要保證一個一個的顯示,不能重疊。還有一些操作完成后,進入到了指定的頁面才顯示這個操作之后的提示或引導彈窗。特別是在 Flutter 中,這些還顯示更為復雜,因為原生的彈窗的優(yōu)先級比 Flutter 的優(yōu)先級高,例如權(quán)限申請、指紋等。
??這就使得處理更為復雜,通用的處理是在界面通些一些變量來控制當前是否有彈窗在顯示,來判斷其它的彈窗要不要顯示,或者通過標識來確認彈窗是否已經(jīng)顯示過;這就使得界面上有各種各樣的臨時變更,及各種判斷,這樣就非常影響整個界面的可讀性。
??所以在這里就把項目中一些處理的思路與方法跟大家分享下,大家一起學習。
現(xiàn)狀
- 彈窗樣式多樣、多個彈窗重疊、多個彈窗間有優(yōu)先級
- 有時彈窗需指定特定頁面
- 同一彈窗會重復顯示
- 彈窗無法指定關(guān)閉
思路
- 對于上述場景,對于自動觸發(fā)彈出的彈窗,統(tǒng)一加到彈窗管理中。
- 對于沒有指定頁面的彈窗,則加入到頂層頁面中。彈窗管理有特定頂層頁面,用于顯示當前頂層需顯示的彈窗列表
- 對于需要在特定頁面顯示的彈窗,則在需要顯示前判斷當前頂層視圖是否為特定頁面(因為 Flutter 中彈窗無需與頁面關(guān)聯(lián))。
- 每個彈窗都有優(yōu)先級,在頁面需要顯示前,會先進行優(yōu)先級排序,再根據(jù)排序結(jié)果進行展示
- 每個彈窗都會生成一個固定的唯一標識,用于處理彈窗重復問題
- 對于彈窗無法指定關(guān)閉的問題,如有需強制顯示的彈窗,則會先判斷當前是否有顯示的彈窗,有則先 pop彈窗再顯示強制的彈窗
實現(xiàn)效果
解決辦法
DialogManager
統(tǒng)一彈窗管理類,管理彈窗的添加及顯示,詳細代碼見最后。
- 初始化方法
??初始化方法中生成兩個數(shù)組List<DialogBean> _dialogList,_hasShowBeans。_dialogList用于儲存加入在統(tǒng)一彈窗中的彈窗數(shù)據(jù)
_hasShowBeans用于存儲已經(jīng)顯示過,還沒有清除的彈窗數(shù)據(jù)
??為什么不用單一 bean 去儲存顯示的彈窗數(shù)據(jù),因為置為空的處理,是在彈窗消失的異步回調(diào)中,如果一個新的彈窗要顯示,舊的那個彈窗消失的回調(diào)過來會把當前在顯示的彈窗數(shù)據(jù)置為空,則會導致當前判斷時否有顯示的彈窗不準確。
- add 方法
主要用于添加彈窗數(shù)據(jù)在_dialogList中
- 假如添加的彈窗為 highClear 或 highClearAll,則會移除其它低優(yōu)先級的彈窗。主要場景是在強制升級彈窗、多終端登錄彈窗
- 對于重復添加的彈窗,則不重復添加
- 對于棧中已經(jīng)存在 highClear 或 highClearAll級別彈窗,則不添加低優(yōu)先級彈窗
- 對所有的彈窗進行優(yōu)先級排序
- show 方法
主要用于添加彈窗數(shù)據(jù)在_dialogList中
- 假如添加的彈窗為 highClear 或 highClearAll,則會移除其它低優(yōu)先級的彈窗。主要場景是在強制升級彈窗、多終端登錄彈窗
- 對于重復添加的彈窗,則不重復添加
- 對于棧中已經(jīng)存在 highClear 或 highClearAll級別彈窗,則不添加低優(yōu)先級彈窗
- 對所有的彈窗進行優(yōu)先級排序
DialogBean
///dialog唯一標識,通過 DialogBean 數(shù)據(jù)內(nèi)容生成 Md5生成
String dialogId;
///當前 dialog,顯示的視圖。如果為空,則在頂層頁面
String pageRouter;
///優(yōu)先級、用于顯示彈窗前排序,
///但對于加入的彈窗,已經(jīng)顯示的情況
///[highClear] 清除已顯示的彈窗,直接顯示當前彈窗,該屬性慎用
///[high] 回收當前已顯示的彈窗,再顯示高優(yōu)先級彈窗,該屬性慎用
DialogPriority dialogPriority;
///用于排序
int priority;
///彈窗內(nèi)部業(yè)務(wù) widget,每次show 時動態(tài)創(chuàng)建。不能直接傳創(chuàng)建好的 widget,因為在 high回收時,調(diào)用 pop 再 show 會出現(xiàn) The following NoSuchMethodError was thrown building Builder(dirty):
CreateDialogWidget createDialogWidg
統(tǒng)一的彈窗實體類,詳細代碼見最后。
- dialogId,用于彈窗去重
- pageRouter用于指定彈窗需顯示的頁面,如果沒有指定,則直接顯示
- dialogPriority標識彈窗優(yōu)先級
- priority彈窗優(yōu)先級擴展字段,主要用于排序
- createDialogWidget生成彈窗widget.不用直接使用 widget,在 high 回收時,再顯示會報異常。
DialogUtil
公共彈窗創(chuàng)建類,詳細代碼見最后。
- 生成對應 dialog 唯一的 id
- dialog 業(yè)務(wù)自身內(nèi)部處理
- 需要加入統(tǒng)一彈窗管理中 dialog 統(tǒng)一在這里生成
場景驗證
多個彈窗是否按順序依次彈出
DialogManager()
..add(
DialogBean(
dialogPriority: DialogPriority.high,
createDialogWidget: () =>
DialogUtil.createTipWidget(context, "測試彈窗\n 換行
"),
))
..add(
DialogBean(
dialogPriority: DialogPriority.high,
createDialogWidget: () =>
DialogUtil.createTipWidget(context, "測試彈窗"),
))
..show(context);
相同的彈窗是否去重
DialogManager()
..add(
DialogBean(
dialogPriority: DialogPriority.high,
createDialogWidget: () =>
DialogUtil.createTipWidget(context, "測試彈窗"),
))
..add(
DialogBean(
dialogPriority: DialogPriority.high,
createDialogWidget: () =>
DialogUtil.createTipWidget(context, "測試彈窗"),
))
..show(context);
彈窗回收:已彈出低優(yōu)先級彈窗,顯示高優(yōu)先級彈窗,是否可以回收顯示
DialogManager()
..add(
DialogBean(
createDialogWidget: () => DialogUtil.createTipWidget(
context,
"測試彈窗\n 換行",
),
))
..show(context);
Future.delayed(Duration(seconds: 1), () {
DialogManager()
..add(
DialogBean(
dialogPriority: DialogPriority.high,
createDialogWidget: () =>
DialogUtil.createTipWidget(context, "測試彈窗"),
))
..show(context);
});
可反過來,先顯示高優(yōu)先級,再顯示其它
彈窗清除:已彈出低優(yōu)先級彈窗,顯示高優(yōu)先級并清除其它的彈窗
DialogManager()
..add(
DialogBean(
createDialogWidget: () => DialogUtil.createTipWidget(
context,
"測試彈窗\n 換行"
),
))
..show(context);
Future.delayed(Duration(seconds: 1), () {
DialogManager()
..add(
DialogBean(
dialogPriority: DialogPriority.highClear,
createDialogWidget: () =>
DialogUtil.createTipWidget(context, "測試彈窗"),
))
..show(context);
});
彈窗頁面驗證:指定彈窗在指定頁面顯示
Future.delayed(Duration(seconds: 1), () {
DialogManager()
..add(
DialogBean(
pageRouter: Router.login,
createDialogWidget: () => DialogUtil.createTipWidget(
context,
"測試彈窗",
),
))
..show(context);
});
最后
??如果在使用過程遇到問題,歡迎下方留言交流。
??代碼下載地址