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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值