FutureBuilder原始碼分析

愛小麗0427發表於2019-06-25

關於 FutureBuilder 的使用,我在之前的公眾號文章中有寫過,

如果沒看過的可以跳轉:Flutter FutureBuilder 非同步UI神器.

FutureBuilder

首先看 FutureBuilder<T> 類。

建構函式

const FutureBuilder({
  Key key,
  this.future,
  this.initialData,
  @required this.builder,
}) : assert(builder != null),
super(key: key);
複製程式碼

建構函式很簡單,上一篇文章也說過,主要就是三個引數:

  • future:是我們的非同步請求,該非同步請求必須不能在 build 方法中初始化!
  • initialData:如果Future 沒有完成的情況下展示該資料
  • builder:構建我們的UI

AsyncWidgetBuilder

其中 builder 的型別為 AsyncWidgetBuilder,我們來看一下:

typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnapshot<T> snapshot);
複製程式碼

其中 typedef 是為函式起別名用的,

也就是說 builder 是一個方法,從而在定義builder的時候就要實現這個方法。

AsyncSnapshot

接著看一下 snapshot

@immutable
class AsyncSnapshot<T> {
  /// Creates an [AsyncSnapshot] with the specified [connectionState],
  /// and optionally either [data] or [error] (but not both).
  const AsyncSnapshot._(this.connectionState, this.data, this.error)
    : assert(connectionState != null),
      assert(!(data != null && error != null));

  /// Creates an [AsyncSnapshot] in [ConnectionState.none] with null data and error.
  const AsyncSnapshot.nothing() : this._(ConnectionState.none, null, null);

  /// Creates an [AsyncSnapshot] in the specified [state] and with the specified [data].
  const AsyncSnapshot.withData(ConnectionState state, T data) : this._(state, data, null);

  /// Creates an [AsyncSnapshot] in the specified [state] and with the specified [error].
  const AsyncSnapshot.withError(ConnectionState state, Object error) : this._(state, null, error);

  /// Current state of connection to the asynchronous computation.
  final ConnectionState connectionState;

  /// The latest data received by the asynchronous computation.
  ///
  /// If this is non-null, [hasData] will be true.
  ///
  /// If [error] is not null, this will be null. See [hasError].
  ///
  /// If the asynchronous computation has never returned a value, this may be
  /// set to an initial data value specified by the relevant widget. See
  /// [FutureBuilder.initialData] and [StreamBuilder.initialData].
  final T data;

  /// Returns latest data received, failing if there is no data.
  ///
  /// Throws [error], if [hasError]. Throws [StateError], if neither [hasData]
  /// nor [hasError].
  T get requireData {
    if (hasData)
      return data;
    if (hasError)
      throw error;
    throw StateError('Snapshot has neither data nor error');
  }

  /// The latest error object received by the asynchronous computation.
  ///
  /// If this is non-null, [hasError] will be true.
  ///
  /// If [data] is not null, this will be null.
  final Object error;

  /// Returns a snapshot like this one, but in the specified [state].
  ///
  /// The [data] and [error] fields persist unmodified, even if the new state is
  /// [ConnectionState.none].
  AsyncSnapshot<T> inState(ConnectionState state) => AsyncSnapshot<T>._(state, data, error);

  /// Returns whether this snapshot contains a non-null [data] value.
  ///
  /// This can be false even when the asynchronous computation has completed
  /// successfully, if the computation did not return a non-null value. For
  /// example, a [Future<void>] will complete with the null value even if it
  /// completes successfully.
  bool get hasData => data != null;

  /// Returns whether this snapshot contains a non-null [error] value.
  ///
  /// This is always true if the asynchronous computation's last result was
  /// failure.
  bool get hasError => error != null;

}
複製程式碼

前面定義了一個私有的建構函式 const AsyncSnapshot._(this.connectionState, this.data, this.error)

後面用命名建構函式來呼叫私有建構函式返回一個 snapshot。

也可以看到 hasData hasError 其實就是判斷 data/error 是否等於 null。

_FutureBuilderState

重點是 _FutureBuilderState<T>,還是從上往下看,

首先定義了兩個私有變數:

/// An object that identifies the currently active callbacks. Used to avoid
/// calling setState from stale callbacks, e.g. after disposal of this state,
/// or after widget reconfiguration to a new Future.
Object _activeCallbackIdentity;
AsyncSnapshot<T> _snapshot;
複製程式碼

_activeCallbackIdentity 根據註釋來解釋大概就是:標記當前還存活的物件,用於避免已經dispose了還呼叫setState。

_snapshot 就是我們剛才說用來返回資料的。

initState()

接著是初始化方法:

@override
void initState() {
  super.initState();
  _snapshot = AsyncSnapshot<T>.withData(ConnectionState.none, widget.initialData);
  _subscribe();
}
複製程式碼

首先根據傳入的 initialData初始化_snapshot,

然後呼叫_subscribe()

_subscribe()

看一下 _subscribe() 方法 :

void _subscribe() {
  if (widget.future != null) {
    final Object callbackIdentity = Object();
    _activeCallbackIdentity = callbackIdentity;
    widget.future.then<void>((T data) {
      if (_activeCallbackIdentity == callbackIdentity) {
        setState(() {
          _snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data);
        });
      }
    }, onError: (Object error) {
      if (_activeCallbackIdentity == callbackIdentity) {
        setState(() {
          _snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error);
        });
      }
    });
    _snapshot = _snapshot.inState(ConnectionState.waiting);
  }
}
複製程式碼

這裡做了如下幾件事:

  1. 判斷 future 是否為null;

  2. 如果不為null,則初始化 _activeCallbackIdentity 為 Object();

  3. 變更 _snapshot 的狀態為 ConnectionState.waiting;

  4. 接著對 Future 呼叫 then 方法,這裡主要就是先判斷了 callbackIdentity是否相等,如果不相等,那麼這個 Future肯定是更改了,或者已經 dispose 了。如果 callbackIdentity 相等,則繼續判斷是有錯誤還是有資料,有資料就呼叫 AsyncSnapshot<T>.withData,有錯誤就呼叫 AsyncSnapshot<T>.withError,並傳入狀態。

didUpdateWidget

接著下面是 didUpdateWidget 方法,該方法主要是用來判斷是否需要更新 widget:

@override
void didUpdateWidget(FutureBuilder<T> oldWidget) {
  super.didUpdateWidget(oldWidget);
  if (oldWidget.future != widget.future) {
    if (_activeCallbackIdentity != null) {
      _unsubscribe();
      _snapshot = _snapshot.inState(ConnectionState.none);
    }
    _subscribe();
  }
}
複製程式碼

這裡更新的邏輯是判斷 future 是否一樣,如果不一樣則:

  1. 判斷 _activeCallbackIdentity 是否為 null
  2. unsubscribe(),取消訂閱。這裡就一行程式碼: _activeCallbackIdentity = null;
  3. 把 _snapshot 的狀態置為 ConnectionState.none
  4. subscribe(),重新訂閱。

dispose()

最後就是 dispose()方法:

@override
void dispose() {
  _unsubscribe();
  super.dispose();
}
複製程式碼

FutureBuilder 重寫該方法來達到 dispose 時自動取消訂閱。

總結

Future 的狀態無非三種:

  1. 未開始
  2. 進行中
  3. 已完成

其中 已完成 又分為兩種:

  1. 有資料
  2. 有異常

其實可以看到,FutureBuilder 大體上的思路就是對 Future 狀態的封裝,從而達到我們想要的效果。

在 Flutter 中,我們可以通過檢視原始碼來獲取很多的靈感,因為 Flutter 的 註釋寫的簡直不要太到位

FutureBuilder原始碼分析

相關文章