最近做了个小功能,要做一个全局的弹窗,随处都可以弹出,这个咋做呢?
说下从头到尾的思路:
- 之前看过文章写过如何不使用context进行路由跳转,正常情况我们都是这么写:
Navigator.of(context).pushNamed('new_page');
都是需要传一个context才可以的。
但有时我们可能需要在没法传context的时候跳转咋写呢?
我们可以这样做:
1 2 3 4 5 6 7 8 9 10 | // 先新建一个navigatorKey GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); //然后,找到我们的MaterialApp MaterialApp( navigatorKey: navigatorKey,//加上此配置 title: 'Flutter Demo', theme: ThemeData.light(), home: HomePage(), ) |
然后我们页面跳转就可以这样写了:
好了,我们实现无context跳转了。
回归正题,既然有了这个state,我们能否用里面的context呢?
然后我就兴奋的去尝试一下:
1 2 3 4 5 6 | showDialog( context: navigatorKey.currentState.context, builder: (context) => AlertDialog( content: Text('content'), ), ) |
结果,很是失望!竟然报错了:
Log:
原来这个context只能用于路由处理,为什么呢?
从调用栈看了下,
最后看到了这里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | static NavigatorState of( BuildContext context, { bool rootNavigator = false, bool nullOk = false, }) { final NavigatorState navigator = rootNavigator ? context.findRootAncestorStateOfType<NavigatorState>() : context.findAncestorStateOfType<NavigatorState>(); assert(() { if (navigator == null && !nullOk) { throw FlutterError( 'Navigator operation requested with a context that does not include a Navigator. ' 'The context used to push or pop routes from the Navigator must be that of a ' 'widget that is a descendant of a Navigator widget.' ); } return true; }()); return navigator; } |
我们看到了错误那个串字符,然后我们拿出核心的:
用过Inheritedxxx的应该比较熟悉这个,是用来从叶子节点,通过context向上查找根组件对象的,这里也就是寻找NavigatorState对象。
但从对应的实现来看,这两种方式查找初始值都是
但也不要失望,至少我们从这次错误中,我们还能从
哦,原来对话框也是一种路由页面,所以我们可以仿照源码改出一份来,这里我就不说了,思路就是push一个自己写的dialogrouter,push进来就好。
- 接下来再介绍第二种方式,浮层:
Overlay 的方式:
Overlay的日常使用,比如popupwindow之类的,使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | final overlay = Overlay.of(context);// 获取一个overlay // 创建一个OverlayEntry OverlayEntry entry = OverlayEntry( builder: (context) { return Material( type: MaterialType.transparency, child: Stack( children: <Widget>[ Positioned( top: 200, left: 200, child: GestureDetector( onTap: () { entry.remove(); entry = null; }, child: Container( color: Colors.redAccent child: Text('hahaha'), width: 100, height: 100, ), ), ), ], ), ); }, ); // 添加进来即可显示 overlay.insert(entry); |
好了,看完这个小demo,我们发现,弹浮窗也需要context。
我们先试一下:
运行后,发现overlay是空,也是不能直接用,为什么呢?
我们看一下Overlay这个Widget在哪初始化的,经过搜索,发现是在Navigator里面build初始化的,也就是说,overlay是Navigator的child。
那经过上面dialog的经验,这里一样的问题,也是找不到的,因为也是从navigator的parent开始的,肯定找不到。
那Overlay该怎么用呢?这个又不是路由,不能push。
说实话,当时我没有思路,我就各种搜啊搜~~
咦,发现了个给力的库,顺便给大家推荐下:bot_toast
支持各种弹框,toast,对话框,通知,跨页面啥的都支持。
用完后,我学习了下他的实现方式,用的就是Overlay的方式,看到他的获取Overlay的方式。
核心就是:使用了NavigatorState里面的overlay对象,我很惊讶,这个navigator里面还有这么个方法?
看了下源码,果然:
那就好说了,我们可以用刚刚那个navigatorKey来获取overlay了,获取方式:
洋洋洒洒写完了,上面是我做这个需求的整个分析及解决思路,大家可以参考下 ^_^。
最后,我们在做全局弹框时,有这两种方式可选,具体看需要哪种合适选哪个吧~~