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

相关推荐

  1. flutter 底部和中间

    2024-05-11 00:14:02       42 阅读
  2. 【WPF】一个并显示一个列表

    2024-05-11 00:14:02       45 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-11 00:14:02       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-11 00:14:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-11 00:14:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-11 00:14:02       20 阅读

热门阅读

  1. c#读取bin文件

    2024-05-11 00:14:02       9 阅读
  2. GOOGLE翻译V3版

    2024-05-11 00:14:02       14 阅读
  3. L6201PSTR DMOS全桥驱动器

    2024-05-11 00:14:02       13 阅读
  4. Oracle 数据库非归档模式迁移数据文件存放位置

    2024-05-11 00:14:02       11 阅读
  5. 12.Netty入门案例

    2024-05-11 00:14:02       9 阅读
  6. libevent 梳理

    2024-05-11 00:14:02       13 阅读
  7. golang 随机数演化

    2024-05-11 00:14:02       10 阅读
  8. c++ 线程的激活和休眠

    2024-05-11 00:14:02       10 阅读
  9. PHP 在字符中找出重复次数最多的字符

    2024-05-11 00:14:02       13 阅读
  10. 算法有哪些分类

    2024-05-11 00:14:02       13 阅读
  11. 变量的细节

    2024-05-11 00:14:02       11 阅读