關於 FutureBuilder
的使用,我在以前的公衆號文章中有寫過,markdown
若是沒看過的能夠跳轉:Flutter FutureBuilder 異步UI神器.異步
首先看 FutureBuilder<T>
類。async
const FutureBuilder({
Key key,
this.future,
this.initialData,
@required this.builder,
}) : assert(builder != null),
super(key: key);
複製代碼
構造函數很簡單,上一篇文章也說過,主要就是三個參數:ide
其中 builder
的類型爲 AsyncWidgetBuilder
,咱們來看一下:函數
typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnapshot<T> snapshot);
複製代碼
其中 typedef
是爲函數起別名用的,ui
也就是說 builder 是一個方法,從而在定義builder的時候就要實現這個方法。this
接着看一下 snapshot
:spa
@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)
,code
後面用命名構造函數來調用私有構造函數返回一個 snapshot。cdn
也能夠看到 hasData
hasError
其實就是判斷 data/error 是否等於 null。
重點是 _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
就是咱們剛纔說用來返回數據的。
接着是初始化方法:
@override
void initState() {
super.initState();
_snapshot = AsyncSnapshot<T>.withData(ConnectionState.none, widget.initialData);
_subscribe();
}
複製代碼
首先根據傳入的 initialData
初始化_snapshot,
而後調用_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);
}
}
複製代碼
這裏作了以下幾件事:
判斷 future 是否爲null;
若是不爲null,則初始化 _activeCallbackIdentity
爲 Object();
變動 _snapshot 的狀態爲 ConnectionState.waiting
;
接着對 Future 調用 then 方法,這裏主要就是先判斷了 callbackIdentity
是否相等,若是不相等,那麼這個 Future確定是更改了,或者已經 dispose 了。若是 callbackIdentity
相等,則繼續判斷是有錯誤仍是有數據,有數據就調用 AsyncSnapshot<T>.withData
,有錯誤就調用 AsyncSnapshot<T>.withError
,並傳入狀態。
接着下面是 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 是否同樣,若是不同則:
_activeCallbackIdentity
是否爲 null_activeCallbackIdentity = null;
,ConnectionState.none
最後就是 dispose()
方法:
@override
void dispose() {
_unsubscribe();
super.dispose();
}
複製代碼
FutureBuilder 重寫該方法來達到 dispose
時自動取消訂閱。
Future 的狀態無非三種:
其中 已完成 又分爲兩種:
其實能夠看到,FutureBuilder 大致上的思路就是對 Future 狀態的封裝,從而達到咱們想要的效果。
在 Flutter 中,咱們能夠經過查看源碼來獲取不少的靈感,由於 Flutter 的 註釋寫的簡直不要太到位!