效果
前言
弹窗的顺序执行在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