常見場景
- 展示請求按鈕
- 使用者點選按鈕,顯示loading
- 展示資料或者錯誤
抽象模式
- 展示請求按鈕(
初始狀態
) - 使用者點選按鈕,顯示loading(
請求中狀態
) - 展示資料或者錯誤 (
結束狀態(成功或失敗)
)
轉換成程式語言
以上三種現實情況對應 AsyncSnapshot
三個狀態
ConnectionState.none
初始態ConnectionState.waiting
請求態ConnectionState.done
完成態snapshot.hasError
完成(異常)snapshot.hasData
完成(正常)
使用 FutureBuilder 處理這個場景
這篇文章的主角,FutureBuilder
就是為了解決這個問題存在的。它接收一個 Future
請求,和對應以上幾種情況的 widget
回撥。即可把資料和介面串聯起來,避免額外宣告僅用來傳遞資料用的變數。
提前宣告瞭一個 _showResult
變數,以表示頁面是否觸發請求。
並且封裝了一個 _fetch()
網路請求。
Future<Map> _fetch() async {
return (await Dio().get("https://jsonplaceholder.typicode.com/users/1"))
.data;
}
複製程式碼
請求的結果是任意的,不管是封裝好的物件,map
,list
,都可以,只要是一個 Future<T>
把這個 Future
呼叫安放到 FutureBuilder
的 future
引數上, 並用 _showResult
來控制何時來觸發這個請求。
FutureBuilder(
future: _showResult ? _fetch() : null,
...
)
複製程式碼
再把每一個 Future
的結果對應的 widget
設定到 builder
引數上:
FutureBuilder(
...
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none: // -------- 初始態
return RaisedButton(
onPressed: () {
setState(() {
_showResult = true; // 點選按鈕,觸發請求
});
},
child: Text("start"),
);
case ConnectionState.waiting: // -------- 請求態
return CircularProgressIndicator();
case ConnectionState.done: // -------- 完成態
if (snapshot.hasError) { // 異常
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else { // 正常
return Text(snapshot.data["name"]);
}
break;
default:
break;
}
return Container();
},
),
複製程式碼
總結
FutureBuilder
把資料請求的 Future<T>
中的資料 T
通過 Builder
的 ConnectionState
衍生出所有可能性,並在每個可能性裡 return
一個 Widgets
。最終實現了 state -> UI 的目的