需求分析 之前在写一个登录页面的 demo 时,点击登录按钮,发送网络请求,并且弹出包含进度条的 Dialog,异步进行的网络请求得到结果后,根据请求结果动态更新 Dialog 的状态
一般的想法就是,使用 setState 状态,但实际情况下,Dialog 的状态并不会得到更新,于是开始查看Dialog 和 showDialog 的源码,在showDialog 的源码中发现了一些端倪:
1 2 3 4 5 6 7 8 9 10 Future<T?> showDialog<T>({ required BuildContext context, }) { return Navigator.of(context, rootNavigator: useRootNavigator).push<T>(DialogRoute<T>( context: context, )); }
在调用了 showDialog
方法后,实际上是进行了路由跳转 的,这是对比安卓原生的一个很大区别,所以用 Navigator.pop
关闭 Dialog;所以当收到请求结果后,进行的 setState 其实更新的是上一个路由界面的状态,也就不会对 Dialog 的状态进行更新。
解决方法 同样的,在dialog.dart
源码 **showDialog **方法的注释部分也获悉到了正统的解决方法:
核心部分的简单翻译就是,**showDialog **的 builder 返回的 widget 没有共享 **showDialog **原来被调用的位置的 context,可以用 StatefulBuilder
或自定义 一个StatefulWidget
来自动更新 Dialog。
StatefulBuilder 的 builder 参数是一个StatefulWidgetBuilder
类型的函数,第一个参数是 context,第二个参数是 setState 函数,也就可以通过这个函数来刷新 Dialog。
1 typedef StatefulWidgetBuilder = Widget Function (BuildContext context, StateSetter setState);
测试代码 我们可以简单模拟一个网络请求操作,来验证该方法是否可行。
构建一个基本的主页面,一个按钮,点击后发起网络操作,显示 Dialog,其_buildLoginDialog()
函数根据标志 flag 返回对应的内容:
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 int flag = 0 ;Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: () { showDialog( context: context, builder: (context) { return StatefulBuilder(builder: (context, state){ if (mounted && flag == 0 ){ _login(state); } return Dialog( child: _buildLoginDialog(), ); }); }); }, child: Text("测试" ), ) ], ), ), ); }
以下是_buildLoginDialog()
代码实现:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 _buildLoginDialog() { if (flag != 0 ) { if (flag < 0 ) { return Container( height: 200 , child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [Text("用户名或密码错误" )], ), ); } else { return Container( height: 200 , child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 50 , height: 50 , child: CircularProgressIndicator(), ), Padding( padding: EdgeInsets.all(13 ), child: Text("登录成功!" ), ) ], ), ); } } else { return Container( height: 200 , child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 50 , height: 50 , child: CircularProgressIndicator(), ), Padding( padding: EdgeInsets.all(13 ), child: Text("登陆中" ), ) ], ), ); } }
需要将 state 函数传入网络请求函数,进行页面状态更新:
1 2 3 4 5 6 7 8 9 10 _login(state) async { print ("请求数据前:" + flag.toString()); await Future.delayed(Duration (seconds: 3 ), () { print ("延时三秒后请求数据" ); }); state(() { flag = 1 ; print ("请求数据后:" + flag.toString()); }); }