關於 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);
}
}
複製程式碼
這裡做了如下幾件事:
-
判斷 future 是否為null;
-
如果不為null,則初始化
_activeCallbackIdentity
為 Object(); -
變更 _snapshot 的狀態為
ConnectionState.waiting
; -
接著對 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 是否一樣,如果不一樣則:
- 判斷
_activeCallbackIdentity
是否為 null - unsubscribe(),取消訂閱。這裡就一行程式碼:
_activeCallbackIdentity = null;
, - 把 _snapshot 的狀態置為
ConnectionState.none
- subscribe(),重新訂閱。
dispose()
最後就是 dispose()
方法:
@override
void dispose() {
_unsubscribe();
super.dispose();
}
複製程式碼
FutureBuilder 重寫該方法來達到 dispose
時自動取消訂閱。
總結
Future 的狀態無非三種:
- 未開始
- 進行中
- 已完成
其中 已完成 又分為兩種:
- 有資料
- 有異常
其實可以看到,FutureBuilder 大體上的思路就是對 Future 狀態的封裝,從而達到我們想要的效果。
在 Flutter 中,我們可以通過檢視原始碼來獲取很多的靈感,因為 Flutter 的 註釋寫的簡直不要太到位!