Flutter 統一彈窗管理的思考與實現

背景

  在項目開發中常常會碰到有時要根據業務後臺接口顯示一些提示框或者引導框什麼的,有時這些彈框要屢次顯示或只顯示一次,而且它們內部也有必定的優先級,還要保證一個一個的顯示,不能重疊。還有一些操做完成後,進入到了指定的頁面才顯示這個操做以後的提示或引導彈窗。特別是在 Flutter 中,這些還顯示更爲複雜,由於原生的彈窗的優先級比 Flutter 的優先級高,例如權限申請、指紋等。git

  這就使得處理更爲複雜,通用的處理是在界面通些一些變量來控制當前是否有彈窗在顯示,來判斷其它的彈窗要不要顯示,或者經過標識來確認彈窗是否已經顯示過;這就使得界面上有各類各樣的臨時變動,及各類判斷,這樣就很是影響整個界面的可讀性。github

  因此在這裏就把項目中一些處理的思路與方法跟你們分享下,你們一塊兒學習。數組

現狀

  1. 彈窗樣式多樣、多個彈窗重疊、多個彈窗間有優先級
  2. 有時彈窗需指定特定頁面
  3. 同一彈窗會重複顯示
  4. 彈窗沒法指定關閉

思路

  • 對於上述場景,對於自動觸發彈出的彈窗,統一加到彈窗管理中。
  • 對於沒有指定頁面的彈窗,則加入到頂層頁面中。彈窗管理有特定頂層頁面,用於顯示當前頂層需顯示的彈窗列表
  • 對於須要在特定頁面顯示的彈窗,則在須要顯示前判斷當前頂層視圖是否爲特定頁面(由於 Flutter 中彈窗無需與頁面關聯)。
  • 每一個彈窗都有優先級,在頁面須要顯示前,會先進行優先級排序,再根據排序結果進行展現
  • 每一個彈窗都會生成一個固定的惟一標識,用於處理彈窗重複問題
  • 對於彈窗沒法指定關閉的問題,若有需強制顯示的彈窗,則會先判斷當前是否有顯示的彈窗,有則先 pop彈窗再顯示強制的彈窗

實現效果

解決辦法

DialogManager

統一彈窗管理類,管理彈窗的添加及顯示,詳細代碼見最後bash

  • 初始化方法

  初始化方法中生成兩個數組List _dialogList,_hasShowBeans。_dialogList用於儲存加入在統一彈窗中的彈窗數據 _hasShowBeans用於存儲已經顯示過,尚未清除的彈窗數據異步

  爲何不用單一 bean 去儲存顯示的彈窗數據,由於置爲空的處理,是在彈窗消失的異步回調中,若是一個新的彈窗要顯示,舊的那個彈窗消失的回調過來會把當前在顯示的彈窗數據置爲空,則會致使當前判斷時否有顯示的彈窗不許確。學習

  • add 方法

主要用於添加彈窗數據在_dialogList中

  1. 假如添加的彈窗爲 highClear 或 highClearAll,則會移除其它低優先級的彈窗。主要場景是在強制升級彈窗、多終端登陸彈窗
  2. 對於重複添加的彈窗,則不重複添加
  3. 對於棧中已經存在 highClear 或 highClearAll級別彈窗,則不添加低優先級彈窗
  4. 對全部的彈窗進行優先級排序
  • show 方法

主要用於添加彈窗數據在_dialogList中測試

  1. 假如添加的彈窗爲 highClear 或 highClearAll,則會移除其它低優先級的彈窗。主要場景是在強制升級彈窗、多終端登陸彈窗
  2. 對於重複添加的彈窗,則不重複添加
  3. 對於棧中已經存在 highClear 或 highClearAll級別彈窗,則不添加低優先級彈窗
  4. 對全部的彈窗進行優先級排序

DialogBean

///dialog惟一標識,經過 DialogBean 數據內容生成 Md5生成
String dialogId;

///當前 dialog,顯示的視圖。若是爲空,則在頂層頁面
String pageRouter;

///優先級、用於顯示彈窗前排序,
///但對於加入的彈窗,已經顯示的狀況
///[highClear] 清除已顯示的彈窗,直接顯示當前彈窗,該屬性慎用
///[high] 回收當前已顯示的彈窗,再顯示高優先級彈窗,該屬性慎用
DialogPriority dialogPriority;

///用於排序
int priority;

///彈窗內部業務 widget,每次show 時動態建立。不能直接傳建立好的 widget,由於在 high回收時,調用 pop 再 show 會出現 The following NoSuchMethodError was thrown building Builder(dirty):
CreateDialogWidget createDialogWidg

複製代碼

統一的彈窗實體類,詳細代碼見最後ui

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

DialogUtil

公共彈窗建立類, 詳細代碼見最後

  1. 生成對應 dialog 惟一的 id
  2. dialog 業務自身內部處理
  3. 須要加入統一彈窗管理中 dialog 統一在這裏生成

場景驗證

1. 多個彈窗是否按順序依次彈出

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);

複製代碼

彈窗回收:已彈出低優先級彈窗,顯示高優先級彈窗,是否能夠回收顯示

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);
});


複製代碼

可反過來,先顯示高優先級,再顯示其它spa

彈窗清除:已彈出低優先級彈窗,顯示高優先級並清除其它的彈窗

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);
});

複製代碼

最後

  若是在使用過程遇到問題,歡迎下方留言交流。3d

  代碼下載地址

學習資料

請你們不吝點贊!由於您的點贊是對我最大的鼓勵,謝謝!

相關文章
相關標籤/搜索