在实战过程中,我们常常遇到需要刷新界面的需求,比如搜索时出现了网络错误,我们为用户提供一个Retry按钮,用户点击后,我们希望整个FutureBuilder
重新加载一下,比如下图:
本页面通过一个FutureBuilder加载搜索结果,同时遇到网络错误时显示以上界面,代码的大致结构如下:
Widget build(BuildContext context) {
Widget build(BuildContext context) {
return FutureBuilder(
future: getSearchResult(),
builder: (context, snapshot) {
// Show user the data
if (snapshot.hasData) {
return showDataPage();
} else if (snapshot.hasError) {
if (isNetworkError(snapshot.error)) {
return NetworkErrorPage();
} else {
return UnknownErrorPage();
}
}
return LoadingPage();
},
);
}
这时,假设我们的NetworkErrorPage
的位置需要添加一个按钮,实现FutureBuilder
重新刷新,一种简单的方法是直接调用该FutureBuilder
所在界面的setState()
方法,每当FutureBuilder
的父Widget
的setState()
方法被调用,都会使得FutureBuilder
重新获取异步数据,代码大致如下:
Widget build(BuildContext context) {
return FutureBuilder(
builder: (context, snapshot) {
// Show user the data
if (snapshot.hasData) {
return showDataPage();
} else if (snapshot.hasError) {
if (isNetworkError(snapshot.error)) {
return ElevatedButton(
onPressed: () {
setState(() {});
},
child: Text('Try Again'),
);
} else {
return UnknownErrorPage();
}
}
return LoadingPage();
},
);
}
但如果我们按照上述结构实现刷新的工作,我们会发现,虽然在我们点击按钮之后,刷新的工作有在进行,但是页面并不会在点击Try Again按钮后重新回到LoadingPage界面,而是等到加载好之后直接更新界面为showDataPage或者Error的相关界面,这是因为snapshot的hasData
和hasError
变量仅仅在每次future任务完成后才会刷新。
如果我们想实现点击Try Again按钮之后实时回到LoadingPage界面的话,可以使用snapshot.connectionState
进行判断,具体代码如下:
FutureBuilder(
future: _future,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return _buildLoader();
}
if (snapshot.hasError) {
return _buildError();
}
if (snapshot.hasData) {
return _buildDataView();
}
return _buildNoData();
});
通过以上方法,即可实现点击按钮后立即回到加载界面,直到下一次加载完成之后再次更新界面的效果。