要了解Future,最应该想到的是:什么是Future?
Future表示在接下来的某个时间的值或错误,借助Future我们可以在Flutter实现异步操作。
Future的2中状态:
Future是dart:async包中的一个类,使用它时需要导入dart:async包
Future常用用法:
/**
* Register callbacks to be called when this future completes.
*
* When this future completes with a value,
* the [onValue] callback will be called with that value.
* If this future is already completed, the callback will not be called
* immediately, but will be scheduled in a later microtask.
*
* If [onError] is provided, and this future completes with an error,
* the `onError` callback is called with that error and its stack trace.
* The `onError` callback must accept either one argument or two arguments
* where the latter is a [StackTrace].
* If `onError` accepts two arguments,
* it is called with both the error and the stack trace,
* otherwise it is called with just the error object.
* The `onError` callback must return a value or future that can be used
* to complete the returned future, so it must be something assignable to
* `FutureOr`.
*
* Returns a new [Future]
* which is completed with the result of the call to `onValue`
* (if this future completes with a value)
* or to `onError` (if this future completes with an error).
*
* If the invoked callback throws,
* the returned future is completed with the thrown error
* and a stack trace for the error.
* In the case of `onError`,
* if the exception thrown is `identical` to the error argument to `onError`,
* the throw is considered a rethrow,
* and the original stack trace is used instead.
*
* If the callback returns a [Future],
* the future returned by `then` will be completed with
* the same result as the future returned by the callback.
*
* If [onError] is not given, and this future completes with an error,
* the error is forwarded directly to the returned future.
*
* In most cases, it is more readable to use [catchError] separately, possibly
* with a `test` parameter, instead of handling both value and error in a
* single [then] call.
*
* Note that futures don't delay reporting of errors until listeners are
* added. If the first `then` or `catchError` call happens after this future
* has completed with an error then the error is reported as unhandled error.
* See the description on [Future].
*/
Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError});
Future参数:
使用上一章:Flutter Http网络请求(3.2)的请求网络数据代码来调试:
/**
* 异步请求网络数据
*/
Future<ChickenSoupBean> _HttpGet() async {
final responce = await http.get("https://v1.hitokoto.cn/");
//UTF-8防止乱码
Utf8Codec utf8codec = Utf8Codec();
//序列化返回数据
final decode = json.decode(utf8codec.decode(responce.bodyBytes));
return ChickenSoupBean.fromJson(decode);
}
InkWell(
onTap: () {
_HttpGet().then((ChickenSoupBean value) {
setState(() {
_mValue = "\n${value.hitokoto}\n---${value.fromWho}";
print("_HttpGetvalue:${value}");
});
}, onError: (onErr) {
print("onError错误${onErr}");
}).catchError((onCatchError) {
print("catchErrorr错误${onCatchError}");
});
},
child: ..... ,
}
Bean类代码
补充:
当执行onError和catchError同时存在时只会调用onError代码
Future.whenComplete相当于Android中的try-catch,try-catch有个finally代码块,Future和Android中的finally模块使用一样.
_HttpGet().then((ChickenSoupBean value) {
setState(() {
_mValue = "\n${value.hitokoto}\n---${value.fromWho}";
print("_HttpGetvalue:${value}");
});
}, onError: (onErr) {
print("onError错误${onErr}");
}).catchError((onCatchError) {
print("catchErrorr错误${onCatchError}");
}).whenComplete(() => {
print("代码执行完啦!!"),
});
Future.delayed是延迟操作
Future.delayed(new Duration(seconds: 2)).then((value) {
print("Future.delayed:我是老大,但我在两秒之后执行的");
});
print("Future.delayed:我是老二,我先执行");
使用还是非常简单,就不解释了,不明白的地方请您在评论区留言哦~
网络请求都是耗时操作,Flutter又是单线程的,所以在网络请求是一定要使用async关键字来标识该方法是异步方法
先上代码和效果在一步步解释吧:
代码:
FutureBuilder<ChickenSoupBean>(
//异步请求的接口
future: _HttpGet(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
//判断snapshot连接状态
if(snapshot.connectionState == ConnectionState.done ){
/**
* 判断数据是否返回成功
* 也可以通过snapshot.hashData 判断数据是否返回成功
*/
if (snapshot.hasError) {
return Text("HasError");
}else{
return Text("${snapshot.data.hitokoto}\n----${snapshot.data.fromWho}");
}
}else{
return CircularProgressIndicator();
}
},
),
Bean类代码
请求方法:
/**
* 异步请求网络数据
*/
Future<ChickenSoupBean> _HttpGet() async {
final responce = await http.get("https://v1.hitokoto.cn/");
//UTF-8防止乱码
Utf8Codec utf8codec = Utf8Codec();
//序列化返回数据
final decode = json.decode(utf8codec.decode(responce.bodyBytes));
return ChickenSoupBean.fromJson(decode);
}
效果:
FutureBuilder参数 | 类型 | 说明 |
---|---|---|
future | Future |
通常情况都是我们的异步方法 |
initialData | T(泛型) | ,简单理解就是初始数据,应该不是很常用 |
builder | AsyncWidgetBuilder |
返回两个参数(BuildContext, AsyncSnapshot |
builder中返回两个参数:
补充:
AsyncSnapshot< dynamic > snapshot有四种状态:
通常情况只需要判断交互完成之后的操作就可以了
数据返回之后FutureBuilder就会自动重新刷新build完成数据的加载,
讲到这里,上面的效果就很好写了
只需要在builder方法中判断是否连接成功,然后在判断是否获取到数据,若没获取到数据则显示一个进度条,获取到数据则显示数据即可.
讲了这么多,可能我讲的不是很明白,还是大家自己动手写一遍记忆会更加深刻,老化说的好看千遍不如手写一遍嘛~
出现了什么问题呢?为什么要进行不必要的重绘?
解:因为项目中有很多地方要使用setState()来重新绘制build,达到数据更新的效果,所以在FutureBuilder()上也会重新绘制,致使网络数据不停地更新,然而更新的还是同样的数据,不禁浪费流量用户体验也不好!
解决思路:
只需要让请求网络数据的代码执行一次,然后重复调用即可,所以需要将请求数据的代码放到initState()中,这样的话他只在页面初始化的时候请求一次.
@override
void initState() {
_httpGet= _HttpGet();
}
FutureBuilder<ChickenSoupBean>(
future : _httpGet,
builder : ......,
)
举例:
在页面添加一个floatingActionButton按钮()用来刷新build
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
});
},
child: Text("刷新"),
),
FutureBuilder<ChickenSoupBean>(
future : _httpGet,
builder : ......,
)
/**
* 异步请求网络数据
*/
Future<ChickenSoupBean> _HttpGet() async {
var responce = await http.get("https://v1.hitokoto.cn/");
//UTF-8防止乱码
Utf8Codec utf8codec = Utf8Codec();
//序列化返回数据
final decode = json.decode(utf8codec.decode(responce.bodyBytes));
return ChickenSoupBean.fromJson(decode);
}
现在FutureBuilder()中的future是使用的initState()初始化的,来看看效果吧:
可以看出,我点击刷新按钮,并不会重新请求接口!
在来看看直接调用_HttpGet()网络请求的方法:
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
});
},
child: Text("刷新"),
),
FutureBuilder<ChickenSoupBean>(
future: _HttpGet(),
builder: .....,
)
/**
* 异步请求网络数据
*/
Future<ChickenSoupBean> _HttpGet() async {
var responce = await http.get("https://v1.hitokoto.cn/");
//UTF-8防止乱码
Utf8Codec utf8codec = Utf8Codec();
//序列化返回数据
final decode = json.decode(utf8codec.decode(responce.bodyBytes));
return ChickenSoupBean.fromJson(decode);
}
因为我这里只获取的是一句话的接口,所以看到请求还是很快的,通常情况下得到的数据是不会变的,而且是一个List,所以还是建议大家这里优化一下
完整代码
上一章:Flutter Http网络请求(3.2)
下一章:Flutter shared_preferences本地存储(3.4)
原创不易,您的点赞就是对我最大的支持,留下您的点赞吧~