前言
大家都知道Flutter的页面是堆栈式管理,通常关闭页面是最后进入的最先关闭,通过pop进行一个退栈操作。
但是我碰到一个问题,有时需要在同一页面上显示多个弹窗。如果此时需要关闭指定的某一个弹窗,那退栈操作明显不合适了,总不能随便关闭最顶级的Dialog
吧。
通过几天的琢磨,终于让我发现可以使用 GlobalKey
和 Navigator
的结合来实现。以下是详细的步骤和原理。
第一步:创建需要关闭弹窗的 GlobalKey
首先,在页面中为每个需要控制的弹窗创建一个 GlobalKey
dart
GlobalKey<NavigatorState> dialogKey1 = GlobalKey();
GlobalKey<NavigatorState> dialogKey2 = GlobalKey();
第二步:将 Key
传递给 showDialog
中的 builder
创建的任意一个小部件
在调用 showDialog
时,将 GlobalKey
传递给弹窗的构建器
dart
_showDialog() {
showDialog(
context: context,
builder: (context) {
return Dialog(
key: dialogKey1,
child: Text('弹窗1'),
);
},
);
}
_showDialog2() {
showDialog(
context: context,
builder: (context) {
return Dialog(
key: dialogKey2,
child: Text('弹窗2'),
);
},
);
}
第三步:关闭指定的弹窗
当需要关闭指定的弹窗时,只需在页面中调用如下方法:
dart
_dismissDialog1() {
if (dialogKey1.currentContext != null) {
final routeDialog = ModalRoute.of(dialogKey1.currentContext!);
if (routeDialog != null) {
Navigator.removeRoute(context, routeDialog);
}
}
}
原理
该方法的原理是,弹窗是通过 Navigator 创建的。通过 Navigator.removeRoute
可以关闭指定路由页面。结合 GlobalKey
获取对应页面的 BuildContext
,再通过 ModalRoute
从上下文中提取出 Route
,并将其传递给 Navigator
进行移除就能达到关闭指定页面的目的。这种方法不仅适用于 Dialog
,也适用于所有通过 Navigator
路由的页面,而且这也不是唯一的方案,通过了解发现可以通过堆栈式管理等方案也可以做到同样的操作
完整示例代码
以下是完整的示例代码,展示如何实现上述功能:
dart
import 'package:flutter/material.dart';
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
GlobalKey<NavigatorState> dialogKey1 = GlobalKey();
GlobalKey<NavigatorState> dialogKey2 = GlobalKey();
@override
void initState() {
super.initState();
// 一秒后连续打开两个 Dialog
Future.delayed(Duration(seconds: 1)).then((_) {
_showDialog();
_showDialog2();
});
// 三秒后仅关闭第一个弹出的 Dialog
Future.delayed(Duration(seconds: 3)).then((_) {
_dismissDialog1();
});
}
_showDialog() {
showDialog(
context: context,
builder: (context) {
return Dialog(
key: dialogKey1,
child: Text('弹窗1'),
);
},
);
}
_showDialog2() {
showDialog(
context: context,
builder: (context) {
return Dialog(
key: dialogKey2,
child: Text('弹窗2'),
);
},
);
}
_dismissDialog1() {
if (dialogKey1.currentContext != null) {
final routeDialog = ModalRoute.of(dialogKey1.currentContext!);
if (routeDialog != null) {
Navigator.removeRoute(context, routeDialog);
}
}
}
@override
Widget build(BuildContext context) {
return Text('弹窗Demo');
}
}
结论
作者对于Flutter的了解也是知之甚少,作为一款目前最优秀的跨平台开发框架,即使碰到问题我也还是会去琢磨琢磨,但是真心希望Flutter别再将大量精力花费在小部件了,几百上千个小部件真学不动了
最后我还有一句话要说
周围二十座雪山,
唯一动弹的
是乌鸫的眼睛。
Wallace Stevens《观察乌鸫的十三种方式》