注意:无特殊说明,Flutter版本及Dart版本以下:程序员
- Flutter版本: 1.12.13+hotfix.5
- Dart版本: 2.7.0
当有一个Future(异步)任务须要展现给用户时,可使用FutureBuilder控件来完成,好比向服务器发送数据成功时显示成功提示:json
var _future = Future.delayed(Duration(seconds: 3), () { return '老孟,一个有态度的程序员'; }); FutureBuilder( future: _future, builder: (context, snapshot) { var widget; if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { widget = Icon( Icons.error, color: Colors.red, size: 48, ); } else { widget = Icon( Icons.check_circle, color: Colors.green, size: 36, ); } } else { widget = Padding( padding: EdgeInsets.all(20), child: CircularProgressIndicator(), ); } return Center( child: Container( height: 100, width: 100, decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.all(Radius.circular(10))), child: widget, ), ); }, );
效果以下:服务器
在Future任务中出现异常如何处理,下面模拟出现异常,修改_future
:网络
var _future = Future.delayed(Duration(seconds: 3), () { return Future.error(''); });
效果以下:异步
builder
是FutureBuilder的构建函数,在这里能够判断状态及数据显示不一样的UI,
ConnectionState的状态包含四种:none
、waiting
、active
、done
,但咱们只须要关注done
状态,此状态表示Future执行完成,snapshot
参数的类型是AsyncSnapshot<T>
。async
FutureBuilder还有一个比较经常使用的场景:网络加载数据并列表展现,这是一个很是常见的功能,在网络请求过程当中显示loading,请求失败时显示失败UI,成功时显示成功UI。ide
模拟成功网络请求,一般会返回json字符串:函数
var _future = Future.delayed(Duration(seconds: 3), () { return 'json 字符串'; });
构建FutureBuilder控件:ui
FutureBuilder( future: _future, builder: (context, snapshot) { var widget; if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { widget = _loadingErrorWidget(); } else { widget = _dataWidget(snapshot.data); } } else { widget = _loadingWidget(); } return widget; }, );
构建loading控件:.net
_loadingWidget() { return Center( child: Padding( padding: EdgeInsets.all(20), child: CircularProgressIndicator(), ), ); }
构建网络加载失败控件:
_loadingErrorWidget() { return Center( child: Text('数据加载失败,请重试。'), ); }
数据加载成功,构建数据展现控件:
_dataWidget(data) { return ListView.separated( itemBuilder: (context, index) { return Container( height: 60, alignment: Alignment.center, child: Text( '$index', style: TextStyle(fontSize: 20), ), ); }, separatorBuilder: (context, index) { return Divider(); }, itemCount: 10, ); }
效果以下:
模拟网络加载失败:
var _future = Future.delayed(Duration(seconds: 3), () { return Future.error(''); });
效果以下:
经过上面的示例说明FutureBuilder控件极大的简化了异步任务相关显示的控件,再也不须要开发者本身维护各类状态以及更新时调用State.setState
。
FutureBuilder是一个StatefulWidget控件,若是在FutureBuilder控件节点的父节点重绘rebuild
,那么FutureBuilder也会重绘,这不只耗费没必要要的资源,若是是网络请求还会消耗用户的流量,这是很是糟糕的体验,如何解决这个问题?
经过源代码发现FutureBuilder重绘逻辑是这样的:
@override void didUpdateWidget(FutureBuilder<T> oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.future != widget.future) { if (_activeCallbackIdentity != null) { _unsubscribe(); _snapshot = _snapshot.inState(ConnectionState.none); } _subscribe(); } }
FutureBuilder在重建时判断旧的future
和新的future
是否相等,若是不相等才会重建,因此咱们只须要让其相等便可,有人可能会觉得设置的future
是同一个函数,以下:
_future() async{ ... } FutureBuilder( future: _future(), ... )
上面的方式是不相等的,是错误的用法,能够将_future方法赋值给变量:
var _mFuture; @override void initState() { // TODO: implement initState super.initState(); _mFuture = _future(); } _future() async{ ... } FutureBuilder( future: _mFuture, ... )
这才是正确的用法。