注意:無特殊說明,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, ... )
這纔是正確的用法。