Flutter Widgets 之 FutureBuilder

注意:無特殊說明,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的狀態包含四種:nonewaitingactivedone,但咱們只須要關注done狀態,此狀態表示Future執行完成,snapshot參數的類型是AsyncSnapshot<T>async

ListView加載網絡數據

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重繪

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,
    ...
)

這纔是正確的用法。

更多相關閱讀:

若是這篇文章有幫助到您,但願您來個「贊」並關注個人公衆號,很是謝謝。

相關文章
相關標籤/搜索