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

效果

前言

弹窗的顺序执行在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("启动弹框链"),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

运行后效果

详情:github.com/yixiaolunhui/flutter_xy

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/600555.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++ Primer 总结索引 | 第十四章:重载运算与类型转换

1、C语言定义了 大量运算符 以及 内置类型的自动转换规则 当运算符 被用于 类类型的对象时&#xff0c;C语言允许我们 为其指定新的含义&#xff1b;也能自定义类类型之间的转换规则 例&#xff1a;可以通过下述形式输出两个Sales item的和&#xff1a; cout << item1 …

信息系统安全与对抗-网络侦查技术与网络扫描技术(期末复习)

1、网络拓扑结构在网络攻击中的作用 查明目标网络的拓扑结构&#xff0c;有利于找到目标网络的关键节点&#xff0c;从而提高攻击效率&#xff0c;达到最大攻击效果。 2、网络侦查在网络攻击中的作用 识别潜在目标系统&#xff0c;确认目标系统适合哪种类型的攻击。 3、百度…

jenkins部署服务到windows系统服务器

1、安装openSSH windows默认不支持ssh协议&#xff0c;需要下载安装&#xff0c;主要适用于jenkins传输文件已经执行命令使用 点击查看下载openSSH 2、项目配置 这里简单说说怎么配置&#xff0c;主要解决点就是ssh执行cmd或shell命令时不能开启新窗口导致应用部署失败或者断…

【机器学习系统的构建】从模型开发的过程讲清楚K-Fold 交叉验证 (Cross-Validation)的原理和应用

0、前言 最近在学习集成学习的时候了解到了k折交叉验证&#xff0c;其实在之前学习吴恩达老师的课程中也学过交叉验证&#xff0c;但是当时也不是很明白。这次借着自己的疑问以及网上搜找资料&#xff0c;终于把交叉验证给弄明白了。 在弄清楚前&#xff0c;我有这样几个疑问…

rancher/elemental 构建不可变IOS(一)

一、什么是elemental Elemental 是 Rancher 的一个变种&#xff0c;专注于提供一个更轻量级的 Kubernetes 发行版。它旨在提供简化的部署和管理体验&#xff0c;同时保持 Kubernetes 的灵活性和强大功能。Elemental 通常针对较小的部署场景或资源受限的环境&#xff0c;例如测…

BFS Ekoparty 2022 -- Linux Kernel Exploitation Challenge

前言 昨天一个师傅给了我一道 linux kernel pwn 题目&#xff0c;然后我看了感觉非常有意思&#xff0c;题目也不算难&#xff08;在看了作者的提示下&#xff09;&#xff0c;所以就花时间做了做&#xff0c;在这里简单记录一下。这个题是 BFS Lab 2022 年的一道招聘题&#…

JavaEE技术之MySql高级-搭建主从复制(主从同步原理、一主多从配置)

文章目录 MySQL主从同步1、MySQL主从同步原理2、一主多从配置2.1、准备主服务器2.2、准备从服务器2.3、启动主从同步2.4、实现主从同步2.5、停止和重置2.6、常见问题问题1问题2 MySQL主从同步 1、MySQL主从同步原理 基本原理&#xff1a; slave会从master读取binlog来进行数据…

软件架构的艺术:探索演化之路上的18大黄金原则

实际工作表明&#xff0c;一步到位的设计往往不切实际&#xff0c;而演化原则指导我们逐步优化架构&#xff0c;以灵活响应业务和技术的变化。这不仅降低了技术债务和重构风险&#xff0c;还确保了软件的稳定性和可扩展性。同时&#xff0c;架构的持续演进促进了团队协作&#…

SQL查询语句(二)逻辑运算关键字

上一篇文章中我们提到了条件查询除了一些简单的数学符号之外&#xff0c;还有一些用于条件判断的关键字&#xff0c;如逻辑判断 关键字AND,OR,NOT和范围查找关键字BETWEEN,IN等&#xff1b;下面我们来介绍一些这些关键字的用法以及他们所表达的含义。 目录 逻辑运算关键字 AND…

HarmonyOS实战开发教程-如何开发一个2048游戏

今天为大家分享的是2048小游戏&#xff0c;先看效果图&#xff1a; 这个项目对于新手友友来说可能有一点难度&#xff0c;但是只要坚持看完一定会有收获。因为小编想分享的并不局限于ArkTs语言&#xff0c;而是编程思想。 这个游戏的基本逻辑是初始化一个4乘4的数组&#xff…

【Toritoise SVN】SVN 怎么忽略文件夹下的所有文件但是不忽略文件夹本身

比如&#xff1a;忽略 Assets\StreamingAssets\LocalAsset文件夹下的所有文件但是不忽略LocalAsset这个文件夹 在TortoiseSVN中&#xff0c;你可以通过以下步骤来修改文件夹的svn:ignore属性&#xff1a; 打开Windows资源管理器&#xff0c;导航到你的工作副本中的Assets\Stre…

Python | Leetcode Python题解之第67题二进制求和

题目&#xff1a; 题解&#xff1a; class Solution:def addBinary(self, a, b) -> str:return {0:b}.format(int(a, 2) int(b, 2))

谷歌发布 HEAL 架构,4 步评估医学 AI 工具是否公平

如果把维持健康状态想象成一场赛跑&#xff0c;并不是所有人都能够站在统一起跑线上&#xff0c;有的人能够平稳的跑完全程&#xff0c;有的人即使跌倒也能够在第一时间获得帮助&#xff0c;但是有些人可能因为经济条件、居住地、教育水平、种族或其他因素而面临更多障碍。 「…

新火种AI|挑战谷歌,OpenAI要推出搜索引擎?

作者&#xff1a;一号 编辑&#xff1a;美美 在AI革新的浪潮下&#xff0c;谷歌搜索迎来了越来越多的“挑战者”。 最近&#xff0c;据多家外媒的消息&#xff0c;有知情人士透露&#xff0c;OpenAI正计划上线一款基于ChatGPT的大型产品&#xff0c;将提供一个新的搜索引擎&…

Ansible自动化运维工具 - playbook 剧本编写

一. inventory 主机清单 Inventory 支持对主机进行分组&#xff0c;每个组内可以定义多个主机&#xff0c;每个主机都可以定义在任何一个或多个主机组内。 1.1 inventory 中的变量含义 Inventory 变量名 含义ansible_hostansible连接节点时的IP地址ansible_port连接对方…

如何搜索空文件夹_名称为(纯或含)中/英/数/符

首先&#xff0c;需要用到的这个工具&#xff1a; 度娘网盘 提取码&#xff1a;qwu2 蓝奏云 提取码&#xff1a;2r1z 打开工具&#xff0c;切换到批量文件复制版块&#xff0c;快捷键Ctrl5 点击右侧的搜索添加 设定要搜索的范围、指定为文件夹、包括子目录&#xff0c;勾选…

【C语言】精品练习题

目录 题目一&#xff1a; 题目二&#xff1a; 题目三&#xff1a; 题目四&#xff1a; 题目五&#xff1a; 题目六&#xff1a; 题目七&#xff1a; 题目八&#xff1a; 题目九&#xff1a; 题目十&#xff1a; 题目十一&#xff1a; 题目十二&#xff1a; 题目十…

OFD(Open Fixed-layout Document)

OFD(Open Fixed-layout Document) &#xff0c;是由工业和信息化部软件司牵头中国电子技术标准化研究院成立的版式编写组制定的版式文档国家标准&#xff0c;属于中国的一种自主格式&#xff0c;要打破政府部门和党委机关电子公文格式不统一&#xff0c;以方便地进行电子文档的…

购物车操作

添加购物车&#xff1a; 需求分析和接口设计&#xff1a; 接口设计&#xff1a; 请求方式&#xff1a;POST 请求路径&#xff1a;/user/shoppingCart/add请求参数&#xff1a;套餐id、菜品id、口味返回结果&#xff1a;code、data、msg 数据库设计&#xff1a; 这上面出现了…

天锐绿盾 | 办公加密系统,源代码防泄密、源代码透明加密、防止开发部门人员泄露源码

天锐绿盾作为一款专注于数据安全与防泄密的专业解决方案&#xff0c;它确实提供了针对源代码防泄密的功能&#xff0c;帮助企业保护其核心的知识产权。 PC地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 以下是天锐绿盾可能采…