flutter dialog异常Another exception was thrown: Navigator operation requested with a context that does not include a Navigator
我在使用flutter里的对话框控件的时候遇到了一个奇怪的错误
Another exception was thrown: Navigator operation requested with a context that does not include a Navigator
研究了一下才知道,flutter里的dialog不是随便就能用的。
原代码如下:
import 'package:flutter/material.dart'; main() {
runApp(new MyApp());
} class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Test',
home: new Scaffold(
appBar: new AppBar(title: new Text('Test')),
body: _buildCenterButton(context),
),
);
}
} Widget _buildCenterButton(BuildContext context) {
return new Container(
alignment: Alignment.center,
child: new Container(
child: _buildButton(context),
));
} Widget _buildButton(BuildContext context) {
return new RaisedButton(
padding: new EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
//padding
child: new Text(
'show dialog',
style: new TextStyle(
fontSize: 18.0, //textsize
color: Colors.white, // textcolor
),
),
color: Theme.of(context).accentColor,
elevation: 4.0,
//shadow
splashColor: Colors.blueGrey,
onPressed: () {
showAlertDialog(context);
});
}
void showAlertDialog(BuildContext context) {
showDialog(
context: context,
builder: (_) => new AlertDialog(
title: new Text("Dialog Title"),
content: new Text("This is my content"),
actions:<Widget>[
new FlatButton(child:new Text("CANCEL"), onPressed: (){
Navigator.of(context).pop(); },),
new FlatButton(child:new Text("OK"), onPressed: (){
Navigator.of(context).pop(); },)
] ));
}
点击按钮的时候没有任何反应,控制台的报错是:
Another exception was thrown: Navigator operation requested with a context that does not include a Navigator
分析下源码吧~
看showDialog方法的源码:
Future<T> showDialog<T>({
@required BuildContext context,
bool barrierDismissible: true,
@Deprecated(
'Instead of using the "child" argument, return the child from a closure '
'provided to the "builder" argument. This will ensure that the BuildContext '
'is appropriate for widgets built in the dialog.'
) Widget child,
WidgetBuilder builder,
}) {
assert(child == null || builder == null);
return Navigator.of(context, rootNavigator: true/*注意这里*/).push(new _DialogRoute<T>(
child: child ?? new Builder(builder: builder),
theme: Theme.of(context, shadowThemeOnly: true),
barrierDismissible: barrierDismissible,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
));
}
Navigator.of 的源码:
static NavigatorState of(
BuildContext context, {
bool rootNavigator: false,
bool nullOk: false,
}) {
final NavigatorState navigator = rootNavigator
? context.rootAncestorStateOfType(const TypeMatcher<NavigatorState>())
: context.ancestorStateOfType(const TypeMatcher<NavigatorState>());
assert(() {
if (navigator == null && !nullOk) {
throw new FlutterError(
'Navigator operation requested with a context that does not include a Navigator.\n'
'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;
}
找到了一模一样的错误信息字符串!看来就是因为Navigator.of(context)抛出了一个FlutterError。
之所以出现这个错误,是因为满足了if (navigator == null && !nullOk) 的条件, 也就是说:
context.rootAncestorStateOfType(const TypeMatcher<NavigatorState>()) 是null。
Navigator.of函数有3个参数,第一个是BuildContext,第二个是rootNavigator,默认为false,可不传,第三个是nullOk,默认为false,可不传。rootNavigator的值决定了是调用ancestorStateOfType还是rootAncestorStateOfType,nullOk的值决定了如果最终结果为null值时该抛出异常还是直接返回一个null。
我们做个测试,传入不同的rootNavigator和nullOk的值,看有什么结果:
void showAlertDialog(BuildContext context) {
try{
debugPrint("Navigator.of(context, rootNavigator=true, nullOk=false)="+
(Navigator.of(context, rootNavigator: true, nullOk: false)).toString());
}catch(e){
debugPrint("error1 " +e.toString());
}
try{
debugPrint("Navigator.of(context, rootNavigator=false, nullOk=false)="+
(Navigator.of(context, rootNavigator: false, nullOk: false)).toString());
}catch(e){
debugPrint("error2 " +e.toString());
}
try{
debugPrint("Navigator.of(context, rootNavigator=false, nullOk=true)="+
(Navigator.of(context, rootNavigator: false, nullOk: true)).toString());
}catch(e){
debugPrint("error3 " +e.toString());
}
//先注释掉showDialog部分的代码
// showDialog(
// context: context,
// builder: (_) => new AlertDialog(
// title: new Text("Dialog Title"),
// content: new Text("This is my content"),
// actions:<Widget>[
// new FlatButton(child:new Text("CANCEL"), onPressed: (){
// Navigator.of(context).pop();
//
// },),
// new FlatButton(child:new Text("OK"), onPressed: (){
// Navigator.of(context).pop();
//
// },)
// ]
//
// ));
}
打印结果:
error1 Navigator operation requested with a context that does not include a Navigator.
error2 Navigator operation requested with a context that does not include a Navigator.
Navigator.of(context, rootNavigator=false, nullOk=true)=null
显然,无论怎么改rootNavigator和nullOk的值,Navigator.of(context, rootNavigator, nullOk)的值都是null。
为什么呢?
rootAncestorStateOfType函数的实现位于framework.dart里,我们可以看一下ancestorStateOfType和rootAncestorStateOfType的区别:
@override
State ancestorStateOfType(TypeMatcher matcher) {
assert(_debugCheckStateIsActiveForAncestorLookup());
Element ancestor = _parent;
while (ancestor != null) {
if (ancestor is StatefulElement && matcher.check(ancestor.state))
break;
ancestor = ancestor._parent;
}
final StatefulElement statefulAncestor = ancestor;
return statefulAncestor?.state;
} @override
State rootAncestorStateOfType(TypeMatcher matcher) {
assert(_debugCheckStateIsActiveForAncestorLookup());
Element ancestor = _parent;
StatefulElement statefulAncestor;
while (ancestor != null) {
if (ancestor is StatefulElement && matcher.check(ancestor.state))
statefulAncestor = ancestor;
ancestor = ancestor._parent;
}
return statefulAncestor?.state;
}
可以看出:
ancestorStateOfType的作用是: 如果某个父元素满足一定条件, 则返回这个父节点的state属性;
rootAncestorStateOfType的作用是: 返回最顶层的满足一定条件的父元素。
这个条件是: 这个元素必须属于StatefulElement , 而且其state属性与参数里的TypeMatcher 相符合。
查询源码可以知道:StatelessWidget 里的元素是StatelessElement,StatefulWidget里的元素是StatefulElement。
也就是说,要想让context.rootAncestorStateOfType(const TypeMatcher<NavigatorState>())的返回值不为null, 必须保证context所在的Widget的顶层Widget属于StatefulWidget(注意是顶层Widget,而不是自己所在的widget。如果context所在的Widget就是顶层Widget,也是不可以的)。
这样我们就大概知道为什么会出错了。我们的showAlertDialog方法所用的context是属于MyApp的, 而MyApp是个StatelessWidget。
那么,修改方案就比较清晰了,我们的对话框所使用的context不能是顶层Widget的context,同时顶层Widget必须是StatefulWidget。
修改后的完整代码如下:
import 'package:flutter/material.dart'; main() {
runApp(new MyApp());
} class MyApp extends StatefulWidget { @override
State<StatefulWidget> createState() {
return new MyState();
}
}
class MyState extends State<MyApp>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Test',
home: new Scaffold(
appBar: new AppBar(title: new Text('Test')),
body: new StatelessWidgetTest(),
),
);
} }
class StatelessWidgetTest extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _buildCenterButton(context);
}
}
Widget _buildCenterButton(BuildContext context) {
return new Container(
alignment: Alignment.center,
child: new Container(
child: _buildButton(context),
));
} Widget _buildButton(BuildContext context) {
return new RaisedButton(
padding: new EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
//padding
child: new Text(
'show dialog',
style: new TextStyle(
fontSize: 18.0, //textsize
color: Colors.white, // textcolor
),
),
color: Theme.of(context).accentColor,
elevation: 4.0,
//shadow
splashColor: Colors.blueGrey,
onPressed: () {
showAlertDialog(context);
});
}
void showAlertDialog(BuildContext context) {
NavigatorState navigator= context.rootAncestorStateOfType(const TypeMatcher<NavigatorState>());
debugPrint("navigator is null?"+(navigator==null).toString()); showDialog(
context: context,
builder: (_) => new AlertDialog(
title: new Text("Dialog Title"),
content: new Text("This is my content"),
actions:<Widget>[
new FlatButton(child:new Text("CANCEL"), onPressed: (){
Navigator.of(context).pop(); },),
new FlatButton(child:new Text("OK"), onPressed: (){
Navigator.of(context).pop(); },)
]
));
}
至于为什么flutter里的对话框控件对BuildContext的要求这么严格,暂时还不清楚原因。
后记:
在flutter里,Widget,Element和BuildContext之间的关系是什么呢?
摘抄部分系统源码如下:
abstract class Element extends DiagnosticableTree implements BuildContext{....} abstract class ComponentElement extends Element {} class StatelessElement extends ComponentElement {
@override
Widget build() => widget.build(this);
} class StatefulElement extends ComponentElement {
@override
Widget build() => state.build(this);
} abstract class Widget extends DiagnosticableTree {
Element createElement();
} abstract class StatelessWidget extends Widget {
@override
StatelessElement createElement() => new StatelessElement(this);
@protected
Widget build(BuildContext context);
} abstract class StatefulWidget extends Widget {
@override
StatefulElement createElement() => new StatefulElement(this);
@protected
State createState();
}
abstract class State<T extends StatefulWidget> extends Diagnosticable {
@protected
Widget build(BuildContext context);
}
flutter dialog异常Another exception was thrown: Navigator operation requested with a context that does not include a Navigator的更多相关文章
- flutter: Another exception was thrown: Navigator operation requested with a context that does not include a Navigator.
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends State ...
- flutter dialog异常Another exception was thrown: No MaterialLocalizations found
flutter dialog异常Another exception was thrown: No MaterialLocalizations found import 'package:flutter ...
- Flutter Navigator operation requested with a context that does not include a Navigat
如下直接在 MaterialApp 中使用 Navigator 是会报 Navigator operation requested with a context that does not inclu ...
- flutter SnackBar异常Another exception was thrown: Scaffold.of() called with a context that does not contain a Scaffold
代码如下: import 'package:flutter/material.dart'; void main() { runApp(MaterialApp( title: 'Returning Da ...
- Could not create the view: An unexpected exception was thrown的解决方法
MyEclipse下面的server窗口突然不能正常显示了,而且还显示Could not create the view: An unexpected exception was thrown(无法创 ...
- Python中的异常(Exception)处理
异常 当你的程序出现例外情况时就会发生异常(Exception).例如,当你想要读取一个文件时,而那个文件却不存在,怎么办?又或者你在程序执行时不小心把它删除了,怎么办?这些通过使用异常来进行处理. ...
- flutter dialog
flutter Dialog import 'dart:math'; import 'package:flutter/material.dart'; import 'test.dart'; impor ...
- [C#] C# 知识回顾 - 你真的懂异常(Exception)吗?
你真的懂异常(Exception)吗? 目录 异常介绍 异常的特点 怎样使用异常 处理异常的 try-catch-finally 捕获异常的 Catch 块 释放资源的 Finally 块 一.异常介 ...
- myEclipse Could not create the view: An unexpected exception was thrown.
myEclipse 非正常关闭,打开后 service Explorer or Package Explorer 视图显示不出来.报“Could not create the view: An une ...
随机推荐
- xrdp远程
安装图形界面 yum groupinstall "GNOME Desktop" 安装epel源 yum install epel* 安装xrdp yum --enablerepo= ...
- scp 远程文件复制命令
scp 远程文件复制工具 1.命令功能 scp用户在不同linux主机间复制文件,他采用ssh协议保障复制的安全性.scp复制是全量完整复制,效率不高,使用与第一次复制,增量复制建议rsync命令. ...
- mysql查看库、表占用存储空间大小
http://blog.csdn.net/bzfys/article/details/55252962 1. 查看该数据库实例下所有库大小,得到的结果是以MB为单位 <span class=&q ...
- 二、Ubuntu16.04安装搜狗wps
1.搜狗: 安装搜狗输入法,下载地址:http://pinyin.sogou.com/linux/(搜索官网下载及安装方法),deb安装方法类似Windows的exe安装,安装后重启生效. 2.wps ...
- 【HDU5289】Assignment
题目大意:给定一个长度为 N 的序列,求序列中最大值和最小值相差小于 K 的连续段的个数. 题解: 最大值和最小值相差不超过 K 是一个在值域角度的限制,应考虑采用平衡树或权值...数据结构进行维护. ...
- Git之协同开发
Github之协同开发 一.协同开发 1.引子:假如三个人共同开发同一份代码,每个人都各自安排了任务,当每个人都完成了一半的时候,提交不提交呢? 要提交,提交到dev吗,都上传了一半,这样回家拿出来的 ...
- HDU-3605-Escape(最大流, 状态压缩)
链接: https://vjudge.net/problem/HDU-3605 题意: 2012 If this is the end of the world how to do? I do not ...
- Codeforces Round #568 (Div. 2) A.Ropewalkers
链接: https://codeforces.com/contest/1185/problem/A 题意: Polycarp decided to relax on his weekend and v ...
- 【JZOJ2156】【2017.7.10普及】复仇者vsX战警之训练
题目 月球上反凤凰装甲在凤凰之力附身霍普之前,将凤凰之力打成五份,分别附身在X战警五大战力上面辐射眼.白皇后.钢力士.秘客和纳摩上(好尴尬,汗). 在凤凰五使徒的至高的力量的威胁下,复仇者被迫逃到昆仑 ...
- vue-router的路由
路由和组件是有区别的:组件一般是在同一个页面的不同模块,但是路由是直接切换到另一个页面,之前的页面销毁. App.vue中的router-view会渲染顶级路由匹配到的组件.组件内部嵌套的router ...