您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
334 行
11 KiB
334 行
11 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.UIWidgets.async;
|
|
using Unity.UIWidgets.foundation;
|
|
|
|
namespace Unity.UIWidgets.widgets {
|
|
public abstract class StreamBuilderBase<T, S> : StatefulWidget {
|
|
public StreamBuilderBase(Key key = null, Stream<T> stream = null) : base(key: key) {
|
|
this.stream = stream;
|
|
}
|
|
|
|
public readonly Stream<T> stream;
|
|
|
|
public abstract S initial();
|
|
|
|
public virtual S afterConnected(S current) => current;
|
|
|
|
public abstract S afterData(S current, T data);
|
|
|
|
public virtual S afterError(S current, object error) => current;
|
|
|
|
public virtual S afterDone(S current) => current;
|
|
|
|
public virtual S afterDisconnected(S current) => current;
|
|
|
|
public abstract Widget build(BuildContext context, S currentSummary);
|
|
|
|
public override State createState() => new _StreamBuilderBaseState<T, S>();
|
|
}
|
|
|
|
class _StreamBuilderBaseState<T, S> : State<StreamBuilderBase<T, S>> {
|
|
StreamSubscription<T> _subscription;
|
|
S _summary;
|
|
|
|
public override void initState() {
|
|
base.initState();
|
|
_summary = widget.initial();
|
|
_subscribe();
|
|
}
|
|
|
|
public override void didUpdateWidget(StatefulWidget statefulWidget) {
|
|
StreamBuilderBase<T, S> oldWidget = statefulWidget as StreamBuilderBase<T, S>;
|
|
if (oldWidget == null) {
|
|
return;
|
|
}
|
|
|
|
base.didUpdateWidget(statefulWidget);
|
|
if (oldWidget != null) {
|
|
if (oldWidget.stream != widget.stream) {
|
|
if (_subscription != null) {
|
|
_unsubscribe();
|
|
_summary = widget.afterDisconnected(_summary);
|
|
}
|
|
|
|
_subscribe();
|
|
}
|
|
}
|
|
}
|
|
|
|
public override Widget build(BuildContext context) => widget.build(context, _summary);
|
|
|
|
public override void dispose() {
|
|
_unsubscribe();
|
|
base.dispose();
|
|
}
|
|
|
|
void _subscribe() {
|
|
if (widget.stream != null) {
|
|
_subscription = widget.stream.listen(
|
|
(T data) => { setState(() => { _summary = widget.afterData(_summary, data); }); },
|
|
onError: (object error, string stackTrace) => {
|
|
setState(() => { _summary = widget.afterError(_summary, error); });
|
|
},
|
|
onDone: () => { setState(() => { _summary = widget.afterDone(_summary); }); });
|
|
_summary = widget.afterConnected(_summary);
|
|
}
|
|
}
|
|
|
|
void _unsubscribe() {
|
|
if (_subscription != null) {
|
|
_subscription.cancel();
|
|
_subscription = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum ConnectionState {
|
|
none,
|
|
|
|
waiting,
|
|
|
|
active,
|
|
|
|
done,
|
|
}
|
|
|
|
//@immutable
|
|
public class AsyncSnapshot<T> : IEquatable<AsyncSnapshot<T>> {
|
|
AsyncSnapshot(ConnectionState connectionState, object data, object error) {
|
|
D.assert(connectionState != null);
|
|
D.assert(!(data != null && error != null));
|
|
this.connectionState = connectionState;
|
|
this.data = (T) data;
|
|
this.error = error;
|
|
}
|
|
|
|
public static AsyncSnapshot<object> nothing() {
|
|
return new AsyncSnapshot<object>(ConnectionState.none, null, null);
|
|
}
|
|
|
|
public static AsyncSnapshot<T> withData(ConnectionState state, T data) {
|
|
return new AsyncSnapshot<T>(state, data, null);
|
|
}
|
|
|
|
public static AsyncSnapshot<T> withError(ConnectionState state, object error) {
|
|
return new AsyncSnapshot<T>(state, null, error);
|
|
}
|
|
|
|
public readonly ConnectionState connectionState;
|
|
|
|
public readonly T data;
|
|
|
|
public T requireData {
|
|
get {
|
|
if (hasData)
|
|
return data;
|
|
if (hasError)
|
|
//TODO: not sure if cast works
|
|
throw (Exception) error;
|
|
throw new Exception("Snapshot has neither data nor error");
|
|
}
|
|
}
|
|
|
|
public readonly object error;
|
|
|
|
public AsyncSnapshot<T> inState(ConnectionState state) {
|
|
return new AsyncSnapshot<T>(state, data, error);
|
|
}
|
|
|
|
public bool hasData {
|
|
get => data != null;
|
|
}
|
|
|
|
public bool hasError {
|
|
get => error != null;
|
|
}
|
|
|
|
public override string ToString() =>
|
|
$"{foundation_.objectRuntimeType(this, "AsyncSnapshot")}({connectionState}, {data}, {error})";
|
|
|
|
public static bool operator ==(AsyncSnapshot<T> left, AsyncSnapshot<T> right) {
|
|
return Equals(left, right);
|
|
}
|
|
|
|
public static bool operator !=(AsyncSnapshot<T> left, AsyncSnapshot<T> right) {
|
|
return !Equals(left, right);
|
|
}
|
|
|
|
public bool Equals(AsyncSnapshot<T> other) {
|
|
if (ReferenceEquals(null, other)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, other)) {
|
|
return true;
|
|
}
|
|
|
|
return connectionState == other.connectionState && EqualityComparer<T>.Default.Equals(data, other.data) &&
|
|
Equals(error, other.error);
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (ReferenceEquals(null, obj)) {
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, obj)) {
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType()) {
|
|
return false;
|
|
}
|
|
|
|
return Equals((AsyncSnapshot<T>) obj);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
unchecked {
|
|
var hashCode = (int) connectionState;
|
|
hashCode = (hashCode * 397) ^ EqualityComparer<T>.Default.GetHashCode(data);
|
|
hashCode = (hashCode * 397) ^ (error != null ? error.GetHashCode() : 0);
|
|
return hashCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static partial class _async {
|
|
public delegate Widget AsyncWidgetBuilder<T>(BuildContext context, AsyncSnapshot<T> snapshot);
|
|
}
|
|
|
|
// TODO(ianh): remove unreachable code above once https://github.com/dart-lang/linter/issues/1139 is fixed
|
|
public class StreamBuilder<T> : StreamBuilderBase<T, AsyncSnapshot<T>> {
|
|
public StreamBuilder(
|
|
_async.AsyncWidgetBuilder<T> builder,
|
|
Key key = null,
|
|
T initialData = default,
|
|
Stream<T> stream = null
|
|
) : base(key: key, stream: stream) {
|
|
D.assert(builder != null);
|
|
this.builder = builder;
|
|
this.initialData = initialData;
|
|
}
|
|
|
|
public readonly _async.AsyncWidgetBuilder<T> builder;
|
|
|
|
public readonly T initialData;
|
|
|
|
|
|
public override
|
|
AsyncSnapshot<T> initial() => AsyncSnapshot<T>.withData(ConnectionState.none, initialData);
|
|
|
|
|
|
public override
|
|
AsyncSnapshot<T> afterConnected(AsyncSnapshot<T> current) => current.inState(ConnectionState.waiting);
|
|
|
|
public override
|
|
AsyncSnapshot<T> afterData(AsyncSnapshot<T> current, T data) {
|
|
return AsyncSnapshot<T>.withData(ConnectionState.active, data);
|
|
}
|
|
|
|
public override
|
|
AsyncSnapshot<T> afterError(AsyncSnapshot<T> current, object error) {
|
|
return AsyncSnapshot<T>.withError(ConnectionState.active, error);
|
|
}
|
|
|
|
public override
|
|
AsyncSnapshot<T> afterDone(AsyncSnapshot<T> current) => current.inState(ConnectionState.done);
|
|
|
|
public override
|
|
AsyncSnapshot<T> afterDisconnected(AsyncSnapshot<T> current) => current.inState(ConnectionState.none);
|
|
|
|
public override
|
|
Widget build(BuildContext context, AsyncSnapshot<T> currentSummary) => builder(context, currentSummary);
|
|
}
|
|
|
|
// TODO(ianh): remove unreachable code above once https://github.com/dart-lang/linter/issues/1141 is fixed
|
|
public class FutureBuilder<T> : StatefulWidget {
|
|
public FutureBuilder(
|
|
_async.AsyncWidgetBuilder<T> builder,
|
|
Key key = null,
|
|
Future<T> future = null,
|
|
T initialData = default
|
|
) :
|
|
base(key: key) {
|
|
D.assert(builder != null);
|
|
this.builder = builder;
|
|
this.future = future;
|
|
this.initialData = initialData;
|
|
}
|
|
|
|
public readonly Future<T> future;
|
|
|
|
public readonly _async.AsyncWidgetBuilder<T> builder;
|
|
|
|
public readonly T initialData;
|
|
|
|
public override
|
|
State createState() => new _FutureBuilderState<T>();
|
|
}
|
|
|
|
class _FutureBuilderState<T> : State<FutureBuilder<T>> {
|
|
object _activeCallbackIdentity;
|
|
AsyncSnapshot<T> _snapshot;
|
|
|
|
public override
|
|
void initState() {
|
|
base.initState();
|
|
_snapshot = AsyncSnapshot<T>.withData(ConnectionState.none, widget.initialData);
|
|
_subscribe();
|
|
}
|
|
|
|
public override
|
|
void didUpdateWidget(StatefulWidget statefulWidget) {
|
|
var oldWidget = statefulWidget as FutureBuilder<T>;
|
|
if (oldWidget == null) {
|
|
return;
|
|
}
|
|
|
|
base.didUpdateWidget(oldWidget);
|
|
if (oldWidget.future != widget.future) {
|
|
if (_activeCallbackIdentity != null) {
|
|
_unsubscribe();
|
|
_snapshot = _snapshot.inState(ConnectionState.none);
|
|
}
|
|
|
|
_subscribe();
|
|
}
|
|
}
|
|
|
|
public override
|
|
Widget build(BuildContext context) => widget.builder(context, _snapshot);
|
|
|
|
public override
|
|
void dispose() {
|
|
_unsubscribe();
|
|
base.dispose();
|
|
}
|
|
|
|
void _subscribe() {
|
|
if (widget.future != null) {
|
|
object callbackIdentity = new object();
|
|
_activeCallbackIdentity = callbackIdentity;
|
|
widget.future.then((object dataIn) => {
|
|
var data = (T) dataIn;
|
|
if (_activeCallbackIdentity == callbackIdentity) {
|
|
setState(() => { _snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data); });
|
|
}
|
|
}, onError: (Exception error) => {
|
|
if (_activeCallbackIdentity == callbackIdentity) {
|
|
setState(() => { _snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error); });
|
|
}
|
|
|
|
return FutureOr.nil;
|
|
});
|
|
_snapshot = _snapshot.inState(ConnectionState.waiting);
|
|
}
|
|
}
|
|
|
|
void _unsubscribe() {
|
|
_activeCallbackIdentity = null;
|
|
}
|
|
}
|
|
}
|