什么是Future?
Future表示在接下來的某個時間的值或錯誤,借助Future我們可以在Flutter實現異步操作。它類似於ES6中的Promise,提供then和catchError的鏈式調用。
Future是dart:async包中的一個類,使用它時需要導入dart:async包,Future有兩種狀態:
- pending - 執行中;
- completed - 執行結束,分兩種情況要么成功要么失敗;
Future的常見用法?
- 使用
future.then獲取future的值與捕獲future的異常 - 結合
async,await future.whenCompletefuture.timeout
使用future.then獲取future的值與捕獲future的異常
import 'dart:async'; Future<String> testFuture() { // throw new Error(); return Future.value('success'); // return Future.error('error'); } main() { testFuture().then((s) { print(s); }, onError: (e) { print('onError:'); print(e); }).catchError((e) { print('catchError:'); print(e); }); }
Future的then的原型:
Future<R> then<R>(FutureOr<R> onValue(T value), {Function onError});
第一個參數會成功的結果回調,第二個參數onError可選表示執行出現異常。
結合async await
Future是異步的,如果我們要將異步轉同步,那么可以借助 async await來完成。
import 'dart:async'; test() async { int result = await Future.delayed(Duration(milliseconds: 2000), () { return Future.value(123); }); print('t3:' + DateTime.now().toString()); print(result); } main() { print('t1:' + DateTime.now().toString()); test(); print('t2:' + DateTime.now().toString()); }
future.whenComplete
有時候我們需要在Future結束的時候做些事情,我們知道then().catchError()的模式類似於try-catch,try-catch有個finally代碼塊,而future.whenComplete就是Future的finally。
import 'dart:async'; import 'dart:math'; void main() { var random = Random(); Future.delayed(Duration(seconds: 3), () { if (random.nextBool()) { return 100; } else { throw 'boom!'; } }).then(print).catchError(print).whenComplete(() { print('done!'); }); }
future.timeout
完成一個異步操作可能需要很長的時間,比如:網絡請求,但有時我們需要為異步操作設置一個超時時間,那么,如何為Future設置超時時間呢?
import 'dart:async'; void main() { new Future.delayed(new Duration(seconds: 3), () { return 1; }).timeout(new Duration(seconds: 2)).then(print).catchError(print); }
運行上述代碼會看到:TimeoutException after 0:00:02.000000: Future not completed
什么是FutureBuilder?
FutureBuilder是一個將異步操作和異步UI更新結合在一起的類,通過它我們可以將網絡請求,數據庫讀取等的結果更新的頁面上。
FutureBuilder的構造方法
FutureBuilder({Key key, Future<T> future, T initialData, @required AsyncWidgetBuilder<T> builder })
future: Future對象表示此構建器當前連接的異步計算;initialData: 表示一個非空的Future完成前的初始化數據;builder: AsyncWidgetBuilder類型的回到函數,是一個基於異步交互構建widget的函數;
這個builder函數接受兩個參數BuildContext context與 AsyncSnapshot<T> snapshot,它返回一個widget。AsyncSnapshot包含異步計算的信息,它具有以下屬性:
connectionState - 枚舉ConnectionState的值,表示與異步計算的連接狀態,ConnectionState有四個值:none,waiting,active和done;
data - 異步計算接收的最新數據;
error - 異步計算接收的最新錯誤對象;
AsyncSnapshot還具有hasData和hasError屬性,以分別檢查它是否包含非空數據值或錯誤值。
現在我們可以看到使用FutureBuilder的基本模式。在創建新的FutureBuilder對象時,我們將Future對象作為要處理的異步計算傳遞。 在構建器函數中,我們檢查connectionState的值,並使用AsyncSnapshot中的數據或錯誤返回不同的窗口小部件。
FutureBuilder的使用?
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; void main() => runApp(new MyApp()); class MyApp extends StatefulWidget { @override State<StatefulWidget> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { String showResult = ''; Future<CommonModel> fetchPost() async { final response = await http .get('http://www.devio.org/io/flutter_app/json/test_common_model.json'); Utf8Decoder utf8decoder = Utf8Decoder(); //fix 中文亂碼 var result = json.decode(utf8decoder.convert(response.bodyBytes)); return CommonModel.fromJson(result); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: Text('Future與FutureBuilder實用技巧'), ), body: FutureBuilder<CommonModel>( future: fetchPost(), builder: (BuildContext context, AsyncSnapshot<CommonModel> snapshot) { switch (snapshot.connectionState) { case ConnectionState.none: return new Text('Input a URL to start'); case ConnectionState.waiting: return new Center(child: new CircularProgressIndicator()); case ConnectionState.active: return new Text(''); case ConnectionState.done: if (snapshot.hasError) { return new Text( '${snapshot.error}', style: TextStyle(color: Colors.red), ); } else { return new Column(children: <Widget>[ Text('icon:${snapshot.data.icon}'), Text('statusBarColor:${snapshot.data.statusBarColor}'), Text('title:${snapshot.data.title}'), Text('url:${snapshot.data.url}') ]); } } }), ), ); } } class CommonModel { final String icon; final String title; final String url; final String statusBarColor; final bool hideAppBar; CommonModel( {this.icon, this.title, this.url, this.statusBarColor, this.hideAppBar}); factory CommonModel.fromJson(Map<String, dynamic> json) { return CommonModel( icon: json['icon'], title: json['title'], url: json['url'], statusBarColor: json['statusBarColor'], hideAppBar: json['hideAppBar'], ); } }
