Flutter 統(tǒng)一彈窗管理的思考與實現(xiàn)

背景

??在項目開發(fā)中經(jīng)常會碰到有時要根據(jù)業(yè)務(wù)后臺接口顯示一些提示框或者引導框什么的,有時這些彈框要多次顯示或只顯示一次,并且它們內(nèi)部也有一定的優(yōu)先級,還要保證一個一個的顯示,不能重疊。還有一些操作完成后,進入到了指定的頁面才顯示這個操作之后的提示或引導彈窗。特別是在 Flutter 中,這些還顯示更為復雜,因為原生的彈窗的優(yōu)先級比 Flutter 的優(yōu)先級高,例如權(quán)限申請、指紋等。

??這就使得處理更為復雜,通用的處理是在界面通些一些變量來控制當前是否有彈窗在顯示,來判斷其它的彈窗要不要顯示,或者通過標識來確認彈窗是否已經(jīng)顯示過;這就使得界面上有各種各樣的臨時變更,及各種判斷,這樣就非常影響整個界面的可讀性。

??所以在這里就把項目中一些處理的思路與方法跟大家分享下,大家一起學習。

現(xiàn)狀

  1. 彈窗樣式多樣、多個彈窗重疊、多個彈窗間有優(yōu)先級
  2. 有時彈窗需指定特定頁面
  3. 同一彈窗會重復顯示
  4. 彈窗無法指定關(guān)閉

思路

  • 對于上述場景,對于自動觸發(fā)彈出的彈窗,統(tǒng)一加到彈窗管理中。
  • 對于沒有指定頁面的彈窗,則加入到頂層頁面中。彈窗管理有特定頂層頁面,用于顯示當前頂層需顯示的彈窗列表
  • 對于需要在特定頁面顯示的彈窗,則在需要顯示前判斷當前頂層視圖是否為特定頁面(因為 Flutter 中彈窗無需與頁面關(guān)聯(lián))。
  • 每個彈窗都有優(yōu)先級,在頁面需要顯示前,會先進行優(yōu)先級排序,再根據(jù)排序結(jié)果進行展示
  • 每個彈窗都會生成一個固定的唯一標識,用于處理彈窗重復問題
  • 對于彈窗無法指定關(guān)閉的問題,如有需強制顯示的彈窗,則會先判斷當前是否有顯示的彈窗,有則先 pop彈窗再顯示強制的彈窗

實現(xiàn)效果

image

解決辦法

DialogManager

image

統(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 方法
image

主要用于添加彈窗數(shù)據(jù)在_dialogList中

  1. 假如添加的彈窗為 highClear 或 highClearAll,則會移除其它低優(yōu)先級的彈窗。主要場景是在強制升級彈窗、多終端登錄彈窗
  2. 對于重復添加的彈窗,則不重復添加
  3. 對于棧中已經(jīng)存在 highClear 或 highClearAll級別彈窗,則不添加低優(yōu)先級彈窗
  4. 對所有的彈窗進行優(yōu)先級排序
  • show 方法

主要用于添加彈窗數(shù)據(jù)在_dialogList中

  1. 假如添加的彈窗為 highClear 或 highClearAll,則會移除其它低優(yōu)先級的彈窗。主要場景是在強制升級彈窗、多終端登錄彈窗
  2. 對于重復添加的彈窗,則不重復添加
  3. 對于棧中已經(jīng)存在 highClear 或 highClearAll級別彈窗,則不添加低優(yōu)先級彈窗
  4. 對所有的彈窗進行優(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)一的彈窗實體類,詳細代碼見最后。

  1. dialogId,用于彈窗去重
  2. pageRouter用于指定彈窗需顯示的頁面,如果沒有指定,則直接顯示
  3. dialogPriority標識彈窗優(yōu)先級
  4. priority彈窗優(yōu)先級擴展字段,主要用于排序
  5. createDialogWidget生成彈窗widget.不用直接使用 widget,在 high 回收時,再顯示會報異常。

DialogUtil

image

公共彈窗創(chuàng)建類,詳細代碼見最后。

  1. 生成對應 dialog 唯一的 id
  2. dialog 業(yè)務(wù)自身內(nèi)部處理
  3. 需要加入統(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);
});

最后

??如果在使用過程遇到問題,歡迎下方留言交流。

??代碼下載地址

學習資料

請大家不吝點贊!因為您的點贊是對我最大的鼓勵,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容