美文网首页
Flutter弹窗链-顺序弹出对话框

Flutter弹窗链-顺序弹出对话框

作者: 一笑轮回吧 | 来源:发表于2024-05-06 17:25 被阅读0次

    效果

    Screen_recording_20240507_164957 (1).gif

    前言

    弹窗的顺序执行在App中是一个比较常见的应用场景。比如进入App首页,一系列的弹窗就会弹出。如果不做处理就会导致弹窗堆积的全部弹出,严重影响用户体验。

    如果多个弹窗中又有判断逻辑,根据点击后需要弹出另一个弹窗,这个弹窗优先级更高,需要在当前弹出框关闭后弹出,又添加了复杂度了,所以才会有需要管理多个弹窗的展示需求。

    实现

    • 采用方式是拦截器法
    /// 源码:https://github.com/yixiaolunhui/flutter_xy
    /// 链式拦截器。
    abstract class ChainInterceptor {
      /// 拦截器执行方法。
      void intercept(ChainHandler chain);
    }
    
    /// 链
    abstract class Chain {
      /// 将拦截器添加到链中。
      /// 如果提供了 [index],则在指定的索引处添加拦截器。
      /// 否则,将拦截器添加到链的末尾。
      void addChain(ChainInterceptor interceptor, {int? index});
    
      /// 执行链
      void proceed();
    }
    
    /// 链状态监听器。
    abstract class ChainStatusListener {
      /// 当链状态发生变化时调用。
      /// [isChainEnd] 表示链是否已经执行完毕。
      void onStatusChange(bool isChainEnd);
    }
    
    /// 构建和执行链帮助类
    class ChainHelper {
      static Builder builder() {
        return Builder();
      }
    }
    
    /// 用于构建链的构建器类。
    class Builder {
      final ChainHandler _chainHandler = ChainHandler();
    
      /// 将拦截器添加到链中。
      ///
      /// 如果提供了 [index],则在指定的索引处添加拦截器。
      /// 否则,将拦截器添加到链的末尾。
      Builder addChain(ChainInterceptor interceptor, {int? index}) {
        _chainHandler.addChain(interceptor, index: index);
        return this;
      }
    
      /// 设置链的状态监听器。
      Builder setChainStatusListener(ChainStatusListener chainStatusListener) {
        _chainHandler.setChainStatusListener(chainStatusListener);
        return this;
      }
    
      /// 获取 [ChainManager] 实例。
      ChainHandler get chainHandler => _chainHandler;
    
      /// 执行链。
      void execute() {
        _chainHandler.proceed();
      }
    }
    
    /// 链管理类
    class ChainHandler implements Chain {
      final _chains = <ChainInterceptor>[];
      int _index = 0;
      ChainStatusListener? _statusListener;
    
      /// 设置链的状态监听器。
      void setChainStatusListener(ChainStatusListener chainStatusListener) {
        _statusListener = chainStatusListener;
      }
    
      /// 将拦截器添加到链中。
      ///
      /// 如果提供了 [index],则在指定的索引处添加拦截器。
      /// 否则,将拦截器添加到链的末尾。
      @override
      void addChain(ChainInterceptor interceptor, {int? index}) {
        if (index != null) {
          _chains.insert(index, interceptor);
        } else {
          _chains.add(interceptor);
        }
      }
    
      /// 获取链中拦截器的数量。
      int getChainCount() => _chains.length;
    
      /// 当前索引
      int get currentIndex => _index;
    
      /// 执行链。
      ///
      /// 通知 [ChainStatusListener] 链状态的变化。
      /// 如果链已经执行完毕,则清空链。
      @override
      void proceed() {
        bool isChainEnd = _index >= _chains.length;
        _statusListener?.onStatusChange(isChainEnd);
        if (isChainEnd) {
          clear();
          return;
        }
        _chains[_index++].intercept(this);
      }
    
      /// 清空链
      void clear() {
        _chains.clear();
        _index = 0;
      }
    }
    
    

    使用

    • 定义多个弹出框
    /// 源码:https://github.com/yixiaolunhui/flutter_xy
    ///第1个弹窗
    class OneDialog implements ChainInterceptor {
      @override
      void intercept(ChainHandler chain) {
        showDialog(
          builder: (BuildContext context) {
            return AlertDialog(
              title: const Text('第1个弹出框'),
              content: const Text('这个是一个弹出框的内容文案'),
              actions: [
                TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                    chain.proceed();
                  },
                  child: const Text('取消'),
                ),
                TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                    chain.proceed();
                  },
                  child: const Text('确认'),
                ),
              ],
            );
          },
          context: App.get().context,
        );
      }
    }
    
    
    ///第2个弹窗
    class TwoDialog implements ChainInterceptor {
      @override
      void intercept(ChainHandler chain) {
        showDialog(
          context: App.get().context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: const Text('第2个弹出框'),
              content: const Text('这个是一个弹出框的内容文案'),
              actions: [
                TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                    chain.proceed();
                  },
                  child: const Text('取消'),
                ),
                TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                    //这里模拟插入新的弹出框,一般场景是点击按钮后,请求网络然后需要弹出新的弹出框
                    chain.addChain(OtherDialog(), index: chain.currentIndex);
                    chain.proceed();
                  },
                  child: const Text('添加其他弹出框'),
                ),
              ],
            );
          },
        );
      }
    }
    
    ///第3个弹窗
    class ThreeDialog implements ChainInterceptor {
      @override
      void intercept(ChainHandler chain) {
        showDialog(
          context: App.get().context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: const Text('第3个弹出框'),
              content: const Text('这个是一个弹出框的内容文案'),
              actions: [
                TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                    chain.proceed();
                  },
                  child: const Text('取消'),
                ),
                TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                    chain.addChain(OtherDialog(), index: chain.currentIndex);
                    chain.proceed();
                  },
                  child: const Text('添加其他弹出框'),
                ),
              ],
            );
          },
        );
      }
    }
    
    ///第4个弹窗
    class FourDialog implements ChainInterceptor {
      @override
      void intercept(ChainHandler chain) {
        showDialog(
          builder: (BuildContext context) {
            return AlertDialog(
              title: const Text('第4个弹出框'),
              content: const Text('这个是一个弹出框的内容文案'),
              actions: [
                TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                    chain.proceed();
                  },
                  child: const Text('取消'),
                ),
                TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                    chain.proceed();
                  },
                  child: const Text('确认'),
                ),
              ],
            );
          },
          context: App.get().context,
        );
      }
    }
    
    
    ///其他弹窗(用于案例中插入用)
    class OtherDialog implements ChainInterceptor {
      @override
      void intercept(ChainHandler chain) {
        showDialog(
          context: App.get().context,
          builder: (BuildContext context) {
            return AlertDialog(
              title: const Text('其他弹出框'),
              content: const Text('这个是一个弹出框的内容文案'),
              actions: [
                TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                    chain.proceed();
                  },
                  child: const Text('取消'),
                ),
                TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                    chain.proceed();
                  },
                  child: const Text('确认'),
                ),
              ],
            );
          },
        );
      }
    }
    
    
    • 如何使用
    class ChainDialogPage extends StatefulWidget {
      const ChainDialogPage({super.key});
    
      @override
      State<ChainDialogPage> createState() => _ChainDialogPageState();
    }
    
    class _ChainDialogPageState extends State<ChainDialogPage> {
      var chainHelper = ChainHelper.builder();
    
      @override
      void initState() {
        super.initState();
      }
    
      //显示对话框
      void showDialogs() {
        chainHelper.addChain(OneDialog());
        chainHelper.addChain(TwoDialog());
        chainHelper.addChain(ThreeDialog());
        chainHelper.addChain(FourDialog());
        chainHelper.execute();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: XYAppBar(
            title: "Flutter弹框链",
            onBack: () {
              Navigator.pop(context);
            },
          ),
          body: SafeArea(
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                    onPressed: () {
                      showDialogs();
                    },
                    child: const Text("启动弹框链"),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    

    运行后效果


    Screen_recording_20240507_164957 (1).gif

    详情:github.com/yixiaolunhui/flutter_xy

    相关文章

      网友评论

          本文标题:Flutter弹窗链-顺序弹出对话框

          本文链接:https://www.haomeiwen.com/subject/uwnlfjtx.html