FutureBuilder
在实际开发中,进入一个页面后执行网络请求加载数据并显示是非常普遍的,这时候我们一般会显示loading直到加载完成显示正常页面。在flutter中我们可以在initState中发起异步请求,然后将请求结果赋值给data,并setState刷新页面,在build中可以这样实现
代码语言:javascript复制if(data == null){
return _LoadingWidget()
}
else{
return ...
}
实际上flutter提供了一个FutureBuilder专门来处理需要异步的组件,下面是一个简单的示例:
代码语言:javascript复制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 = Text("出错了");
} else {
widget = Text(snapshot.data);
}
} else {
widget = Padding(
padding: EdgeInsets.all(20),
child: CircularProgressIndicator(),
);
}
return Center(
child: widget,
);
},
);
可以看到FutureBuilder有两个主要属性
- future:异步处理的任务。比如请求数据,读取文件等等
- builder:创建widget。其中它的snapshot是该组件当前的状态,我们通过它来实现组件的切换。
snapshot的connectionState表示异步任务的状态,如果是ConnectionState.done表示任务完成,这时候通过snapshot.hasError来区分是出错(显示错误)还是正常完成(显示数据);否则就表示任务在执行中(显示laoding)。我们通过这些状态来返回不同的组件来实现异步加载的过程。
当任务正常完成(ConnectionState.done且snapshot.hasError为false)时,我们可以通过snapshot.data来获取异步返回的数据,再渲染页面即可。
防止FutureBuilder重绘
FutureBuilder是一个StatefulWidget控件,如果父节点重绘rebuild那么FutureBuilder也会重绘,但是这时候可能我们根本不是要请求数据,可能仅仅是更新页面上的一个文案,这样就会造成不必要的浪费和消耗,我们要尽量避免,所以就需要防止FutureBuilder重绘。
FutureBuilder重绘源码如下:
代码语言:javascript复制@override
void didUpdateWidget(FutureBuilder<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.future != widget.future) {
if (_activeCallbackIdentity != null) {
_unsubscribe();
_snapshot = _snapshot.inState(ConnectionState.none);
}
_subscribe();
}
}
可以看到它是判断futrue是否是同一个对象来执行重绘的,所以我们只要提前将异步任务的函数赋值给一个变量,然后在FutureBuilder中使用这个变量即可,如下:
代码语言:javascript复制var _mFuture;
@override
void initState() {
// TODO: implement initState
super.initState();
_mFuture = _future();
}
_future() async{
...
}
FutureBuilder(
future: _mFuture,
...
)
这样重绘的时候因为是同一个对象,所以FutureBuilder不会重绘,减少了不必要的资源损耗。