Flutter实现弹窗拦截器的示例代码
作者:newki
前言
弹窗的排队执行在App中是一个很常见的应用场景。比如进入App首页,一系列的弹窗就会弹出。如果不做处理就会导致弹窗一窝蜂的全部弹出,严重影响用户体验。
如果多个弹窗中有判断逻辑,同意协议弹出另一个弹窗,不同意弹窗另外一个弹窗,又添加了复杂度了。
所以才会有需要管理多个弹窗的展示时机,一般来说在一个Flutter App 中有下面几种方式来管理的。
一、队列的方式实现
其实最简单最原始的方案就是硬写,判断逻辑写在弹窗或Picker里面,一层一层的嵌套,反正布局都嵌套了,弹窗逻辑嵌套怕什么...
当然比较好的做法是用一个队列管理起来。
class DialogQueue { final Queue<AlertDialog> _dialogQueue = Queue(); bool _isShowingDialog = false; void show(AlertDialog dialog, BuildContext context) { _dialogQueue.add(dialog); if (!_isShowingDialog) { _isShowingDialog = true; _showNext(context); } } void _showNext(BuildContext context) { if (_dialogQueue.isNotEmpty) { final dialog = _dialogQueue.removeFirst(); showDialog<void>( context: context, builder: (BuildContext context) { return dialog; }, ).then((_) { if (_dialogQueue.isNotEmpty) { _showNext(context); } else { _isShowingDialog = false; } }); } } }
使用的时候,往队列里面添加即可:
ElevatedButton( onPressed: () { _dialogQueue.show( AlertDialog( title: Text('Dialog 1'), content: Text('This is the first dialog'), ), context, ); }, child: Text('Show Dialog 1'), ), ElevatedButton( onPressed: () { _dialogQueue.show( AlertDialog( title: Text('Dialog 2'), content: Text('This is the second dialog'), ), context, ); }, child: Text('Show Dialog 2'), ),
代码很简单,但这样虽然能做到排队执行,但是有几个很大的问题,不够灵活,不能控制其中哪一个不显示或不显示(实现起来比较麻烦),其次就是只支持 Dialog,不支持用 Toast SnackBar Picker等其他的用法。
总结一句话就是不够灵活。那么我们之前使用过值拦截式的拦截器实现在 Android 平台已经实现过了。那么我们移植到 Flutter 平台能不能实现对应的功能呢?
二、拦截器的方式实现
与前文拦截器实现登录拦截的方式有所区别的是通过值传递方式我们需要定义一个泛型,把传递的对象定义一下。
class InterceptChain<T> { InterceptChain? next; void intercept(T data) { next?.intercept(data); } }
管理类:
class InterceptChainHandler<T> { InterceptChain? _interceptFirst; void add(InterceptChain interceptChain) { if (_interceptFirst == null) { _interceptFirst = interceptChain; return; } InterceptChain? node = _interceptFirst; while (true) { if (node!.next == null) { node.next = interceptChain; break; } node = node.next; } } void intercept(T data) { _interceptFirst?.intercept(data); } }
与之前拦截登录的方式不同的是,这种方案没有指定的一些拦截器,更加的灵活,使用起来我们就需要定义每一个 InterceptChain ,也就是每一个 Dialog/Picker等效果都可以是一个 Intercept 。
class TipsIntercept extends InterceptChain<DialogPass> { @override void intercept(DialogPass data) { SmartDialog.show( usePenetrate: false, builder: (context) => AppDefaultDialog( "你确定要发送验证码吗?", confirmAction: () { super.intercept(data); }, ), ); } } class VerifyIntercept extends InterceptChain<DialogPass> { @override void intercept(DialogPass data) { SmartDialog.show( usePenetrate: false, builder: (context) => VerifyCodeDialog( confirmAction: () { super.intercept(data); }, ), ); } } class OneIntercept extends InterceptChain<DialogPass> { @override void intercept(DialogPass data) { if (data != null) { final interceptData = '当前的Data1:${data.msg}'; print(interceptData); if (data.passType == 1) { // 弹窗1 SmartDialog.show( usePenetrate: false, builder: (context) => AppDefaultDialog( data.msg ?? "", confirmAction: () { data.passType = 2; super.intercept(data); }, ), ); } else { super.intercept(data); } } else { super.intercept(data); } } } class TwoIntercept extends InterceptChain<DialogPass> { @override void intercept(DialogPass data) { if (data != null) { final interceptData = '当前的Data2:${data.msg}'; print(interceptData); if (data.passType == 2) { // 弹窗2 SmartDialog.show( usePenetrate: false, clickMaskDismiss: false, builder: (context) => PrivacyPolicyDialog( confirmAction: () { data.passType = 3; super.intercept(data); }, cancelAction: () { super.intercept(data); }, ), ); } else { super.intercept(data); } } else { super.intercept(data); } } } class ThreeIntercept extends InterceptChain<DialogPass> { @override void intercept(DialogPass data) { if (data != null) { final interceptData = '当前的Data3:${data.msg}'; print(interceptData); if (data.passType == 3) { // 弹窗2 DataPickerUtil.showCupertinoDataPicker( items: ["全部","等待确认", "已完成"], onPickerChanged: (list, position) { super.intercept(data); }, ); } else { super.intercept(data); } } else { super.intercept(data); } } }
这里简单的定义了5个弹窗/Picker效果,并且简单的定义了变量的控制对象为:
class DialogPass { String? msg; int passType = 0; DialogPass(this.msg, this.passType); }
这个是一般是由后台服务器控制,请求接口之后可以动态的根据多个变量控制哪一个弹窗展示,并且可以控制一个弹窗展示之后它对应的后续弹窗的展示。可以很方便的通过对象中的变量控制。
具体哪个弹窗是否展示,如何展示都是子 Intercept 控制,我们最终只需要把全部的拦截类添加到拦截器容器中即可:
final intercepts = InterceptChainHandler<DialogPass>(); intercepts.add(TipsIntercept()); intercepts.add(VerifyIntercept()); intercepts.add(OneIntercept()); intercepts.add(TwoIntercept()); intercepts.add(ThreeIntercept()); intercepts.intercept(DialogPass('你确定你要阅读全部协议吗1?',1));
效果:
虽然代码是多一个但是相对而言就灵活一点。
后记
用队列虽然也能实现一些特定的场景需求,但是我还是更推荐拦截器的方式,相对而言更灵活。
所以说一些在 iOS/Android 中成熟使用的一些设计模式真的是可以很方便的解决一些业务场景的痛点,并不限制于语言与平台。
现在我们应用中的首页的弹窗与申请工作的弹窗校验就能很方便的用拦截器的方式进行管理了。并且用值传递的方式我们也可以很方便的把最终处理过后的数据拿到可以进行其他的拦截或操作。
到此这篇关于Flutter实现弹窗拦截器的示例代码的文章就介绍到这了,更多相关Flutter弹窗拦截器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!