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