• <ul id="cgeq2"></ul>
  • 歡迎您光臨深圳塔燈網絡科技有限公司!
    電話圖標 余先生:13699882642

    網站百科

    為您解碼網站建設的點點滴滴

    flutter demo (四):對話框

    發表日期:2018-08 文章編輯:小燈 瀏覽次數:2784

    我在使用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。大致意思是,context里沒有Navigator對象,卻做了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里,我們可以看一下ancestorStateOfTyperootAncestorStateOfType的區別:

    @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();},) ] )); }

    實驗結果:

    screen1.png

    至于為什么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); }

    本頁內容由塔燈網絡科技有限公司通過網絡收集編輯所得,所有資料僅供用戶學習參考,本站不擁有所有權,如您認為本網頁中由涉嫌抄襲的內容,請及時與我們聯系,并提供相關證據,工作人員會在5工作日內聯系您,一經查實,本站立刻刪除侵權內容。本文鏈接:http://www.juherenli.com/17573.html
    相關APP開發
     八年  行業經驗

    多一份參考,總有益處

    聯系深圳網站公司塔燈網絡,免費獲得網站建設方案及報價

    咨詢相關問題或預約面談,可以通過以下方式與我們聯系

    業務熱線:余經理:13699882642

    Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.    

    99久久99这里只有免费的精品| 久久国产精品成人片免费| 国产成人精品日本亚洲18图| 久久久久成人精品无码| 99精品在线免费观看| 国产精品美女WWW爽爽爽视频| 天天拍夜夜添久久精品| 久久99精品久久久久久青青日本| 亚洲精品综合久久| 久久精品国产72国产精| 99视频有精品视频免费观看| 亚洲中文久久精品无码ww16| 西瓜精品国产自在现线| 国产精品视频在线观看| 华人在线精品免费观看| 丰满人妻熟妇乱又伦精品软件| 久久亚洲精品无码网站| 2022国产成人精品福利网站| 国产在线观看91精品不卡| 日本精品视频一视频高清| 中国国产成人精品久久| 精品一区二区三区在线播放 | 91精品福利视频| 亚洲精品二区国产综合野狼 | 国内精品videofree720| 第一福利永久视频精品| 国产精品扒开腿做爽爽爽的视频 | 国产亚洲精品精华液| 久久国产精品视频| 国产精品亚洲高清一区二区| 亚洲精品成a人在线观看| 北岛玲在线精品视频| 视频一区二区精品的福利| 国产一精品一aⅴ一免费| 国产精品自产拍在线网站| 久久精品午夜一区二区福利| 久久精品99国产精品日本| 久久99精品国产99久久6男男| 国产丝袜在线精品丝袜| 国产精品一区二区资源| 国产精品美女久久久网站动漫|