用 FutureBuilder 提高開發效率

darrenzheng發表於2019-07-02

常見場景

  1. 展示請求按鈕
  2. 使用者點選按鈕,顯示loading
  3. 展示資料或者錯誤

用 FutureBuilder 提高開發效率

抽象模式

  1. 展示請求按鈕(初始狀態
  2. 使用者點選按鈕,顯示loading(請求中狀態
  3. 展示資料或者錯誤 (結束狀態(成功或失敗)

轉換成程式語言

以上三種現實情況對應 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;
}
複製程式碼

請求的結果是任意的,不管是封裝好的物件,maplist,都可以,只要是一個 Future<T>

把這個 Future 呼叫安放到 FutureBuilderfuture 引數上, 並用 _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 通過 BuilderConnectionState 衍生出所有可能性,並在每個可能性裡 return 一個 Widgets。最終實現了 state -> UI 的目的

相關文章