Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
fbd4bb91
Unverified
Commit
fbd4bb91
authored
Jul 21, 2019
by
Todd Volkert
Committed by
GitHub
Jul 21, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert "AsyncSnapshot.data to throw if error or no data (#34626)" (#36618)
This reverts commit
b61fcfd2
.
parent
b680270c
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
184 additions
and
441 deletions
+184
-441
async.dart
packages/flutter/lib/src/widgets/async.dart
+87
-283
async_test.dart
packages/flutter/test/widgets/async_test.dart
+97
-158
No files found.
packages/flutter/lib/src/widgets/async.dart
View file @
fbd4bb91
...
...
@@ -59,41 +59,35 @@ abstract class StreamBuilderBase<T, S> extends StatefulWidget {
///
/// Sub-classes must override this method to provide the initial value for
/// the fold computation.
@protected
S
initial
();
/// Returns an updated version of the [current] summary reflecting that we
/// are now connected to a stream.
///
/// The default implementation returns [current] as is.
@protected
S
afterConnected
(
S
current
)
=>
current
;
/// Returns an updated version of the [current] summary following a data event.
///
/// Sub-classes must override this method to specify how the current summary
/// is combined with the new data item in the fold computation.
@protected
S
afterData
(
S
current
,
T
data
);
/// Returns an updated version of the [current] summary following an error.
///
/// The default implementation returns [current] as is.
@protected
S
afterError
(
S
current
,
Object
error
)
=>
current
;
/// Returns an updated version of the [current] summary following stream
/// termination.
///
/// The default implementation returns [current] as is.
@protected
S
afterDone
(
S
current
)
=>
current
;
/// Returns an updated version of the [current] summary reflecting that we
/// are no longer connected to a stream.
///
/// The default implementation returns [current] as is.
@protected
S
afterDisconnected
(
S
current
)
=>
current
;
/// Returns a Widget based on the [currentSummary].
...
...
@@ -191,8 +185,6 @@ enum ConnectionState {
/// Immutable representation of the most recent interaction with an asynchronous
/// computation.
///
/// `T` is the type of computation data.
///
/// See also:
///
/// * [StreamBuilder], which builds itself based on a snapshot from interacting
...
...
@@ -201,68 +193,46 @@ enum ConnectionState {
/// with a [Future].
@immutable
class
AsyncSnapshot
<
T
>
{
/// Creates an [AsyncSnapshot] with the specified [connectionState] and
/// [hasData], and optionally either [data] or [error] (but not both).
///
/// It is legal for both [hasData] to be true and [data] to be null.
const
AsyncSnapshot
.
_
(
this
.
connectionState
,
this
.
hasData
,
this
.
_data
,
this
.
error
)
/// 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
(
hasData
!=
null
),
assert
(
hasData
||
_data
==
null
),
assert
(!(
hasData
&&
error
!=
null
));
assert
(!(
data
!=
null
&&
error
!=
null
));
/// Creates an [AsyncSnapshot] in the specified [state] and with neither
/// [data] nor [error].
const
AsyncSnapshot
.
withoutData
(
ConnectionState
state
)
:
this
.
_
(
state
,
false
,
null
,
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] (possibly null).
const
AsyncSnapshot
.
withData
(
ConnectionState
state
,
T
data
)
:
this
.
_
(
state
,
true
,
data
,
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
,
false
,
null
,
error
);
/// Creates an [AsyncSnapshot] in the specified [state] and with the specified [error].
const
AsyncSnapshot
.
withError
(
ConnectionState
state
,
Object
error
)
:
this
.
_
(
state
,
null
,
error
);
/// The current state of the connection to the asynchronous computation.
///
/// This property exists independently of the [data] and [error] properties.
/// In other words, a snapshot can exist with any combination of
/// (`connectionState`/`data`) or (`connectionState`/`error`) tuples.
///
/// This is guaranteed to be non-null.
/// Current state of connection to the asynchronous computation.
final
ConnectionState
connectionState
;
///
Whether this snapshot contains [data]
.
///
The latest data received by the asynchronous computation
.
///
/// This can be false even when the asynchronous computation has completed
/// successfully ([connectionState] is [ConnectionState.done]), if the
/// computation did not return a value. For example, a [Future<void>] will
/// complete with no data even if it completes successfully.
/// If this is non-null, [hasData] will be true.
///
/// If this property is false, then attempting to access the [data] property
/// will throw an exception.
final
bool
hasData
;
/// The latest data received by the asynchronous computation, failing if
/// there is no data.
///
/// If [hasData] is true, accessing this property will not throw an error.
/// If [error] is not null, this will be null. See [hasError].
///
/// If [error] is not null, attempting to access this property will throw
/// [error]. 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.
///
///
If neither [hasData] nor [hasError] is true, then accessing this
///
property will throw a [State
Error].
T
get
d
ata
{
///
Throws [error], if [hasError]. Throws [StateError], if neither [hasData]
///
nor [has
Error].
T
get
requireD
ata
{
if
(
hasData
)
return
_data
;
if
(
hasError
)
{
// TODO(tvolkert): preserve the stack trace (https://github.com/dart-lang/sdk/issues/30741)
return
data
;
if
(
hasError
)
throw
error
;
}
throw
StateError
(
'Snapshot has neither data nor error'
);
}
final
T
_data
;
/// The latest error object received by the asynchronous computation.
///
...
...
@@ -273,46 +243,41 @@ class AsyncSnapshot<T> {
/// Returns a snapshot like this one, but in the specified [state].
///
/// The [hasData], [data], [hasError], and [error] fields persist unmodified,
/// even if the new state is [ConnectionState.none].
AsyncSnapshot
<
T
>
inState
(
ConnectionState
state
)
=>
AsyncSnapshot
<
T
>.
_
(
state
,
hasData
,
_data
,
error
);
/// 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.
///
/// When this is true, [hasData] will always be false.
bool
get
hasError
=>
error
!=
null
;
@override
String
toString
()
{
final
StringBuffer
buffer
=
StringBuffer
()..
write
(
'
$runtimeType
'
)
..
write
(
'('
)
..
write
(
'
$connectionState
'
);
if
(
hasData
)
buffer
.
write
(
', data:
$_data
'
);
else
if
(
hasError
)
buffer
.
write
(
', error:
$error
'
);
buffer
.
write
(
')'
);
return
buffer
.
toString
();
}
String
toString
()
=>
'
$runtimeType
(
$connectionState
,
$data
,
$error
)'
;
@override
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
runtimeType
!=
other
.
runtimeType
)
if
(
other
is
!
AsyncSnapshot
<
T
>
)
return
false
;
final
AsyncSnapshot
<
T
>
typedOther
=
other
;
return
connectionState
==
typedOther
.
connectionState
&&
hasData
==
typedOther
.
hasData
&&
_data
==
typedOther
.
_data
&&
data
==
typedOther
.
data
&&
error
==
typedOther
.
error
;
}
@override
int
get
hashCode
=>
hashValues
(
connectionState
,
hasData
,
_
data
,
error
);
int
get
hashCode
=>
hashValues
(
connectionState
,
data
,
error
);
}
/// Signature for strategies that build widgets based on asynchronous
...
...
@@ -342,12 +307,12 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
/// of the following snapshots that includes the last one (the one with
/// ConnectionState.done):
///
/// * `AsyncSnapshot<int>.withData(ConnectionState.waiting, null)`
/// * `AsyncSnapshot<int>.withData(ConnectionState.active, 0)`
/// * `AsyncSnapshot<int>.withData(ConnectionState.active, 1)`
/// * `
new
AsyncSnapshot<int>.withData(ConnectionState.waiting, null)`
/// * `
new
AsyncSnapshot<int>.withData(ConnectionState.active, 0)`
/// * `
new
AsyncSnapshot<int>.withData(ConnectionState.active, 1)`
/// * ...
/// * `AsyncSnapshot<int>.withData(ConnectionState.active, 9)`
/// * `AsyncSnapshot<int>.withData(ConnectionState.done, 9)`
/// * `
new
AsyncSnapshot<int>.withData(ConnectionState.active, 9)`
/// * `
new
AsyncSnapshot<int>.withData(ConnectionState.done, 9)`
///
/// The actual sequence of invocations of the [builder] depends on the relative
/// timing of events produced by the stream and the build rate of the Flutter
...
...
@@ -364,7 +329,7 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
///
/// The stream may produce errors, resulting in snapshots of the form:
///
/// * `AsyncSnapshot<int>.withError(ConnectionState.active, 'some error')`
/// * `
new
AsyncSnapshot<int>.withError(ConnectionState.active, 'some error')`
///
/// The data and error fields of snapshots produced are only changed when the
/// state is `ConnectionState.active`.
...
...
@@ -372,20 +337,7 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
/// The initial snapshot data can be controlled by specifying [initialData].
/// This should be used to ensure that the first frame has the expected value,
/// as the builder will always be called before the stream listener has a chance
/// to be processed. In cases where callers wish to have no initial data, the
/// [new StreamBuilder.withoutInitialData] constructor may be used. Doing so
/// may cause the first frame to have a snapshot that contains no data.
///
/// ## Void StreamBuilders
///
/// The `StreamBuilder<void>` type will produce snapshots that contain no data.
/// An example stream of snapshots would be the following:
///
/// * `AsyncSnapshot<void>.withoutData(ConnectionState.waiting)`
/// * `AsyncSnapshot<void>.withoutData(ConnectionState.active)`
/// * ...
/// * `AsyncSnapshot<void>.withoutData(ConnectionState.active)`
/// * `AsyncSnapshot<void>.withoutData(ConnectionState.done)`
/// to be processed.
///
/// {@tool sample}
///
...
...
@@ -396,7 +348,6 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
/// ```dart
/// StreamBuilder<int>(
/// stream: _lot?.bids, // a Stream<int> or null
/// initialData: 100, // initial seed value
/// builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
/// if (snapshot.hasError)
/// return Text('Error: ${snapshot.error}');
...
...
@@ -421,95 +372,42 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
// TODO(ianh): remove unreachable code above once https://github.com/dart-lang/linter/issues/1139 is fixed
class
StreamBuilder
<
T
>
extends
StreamBuilderBase
<
T
,
AsyncSnapshot
<
T
>>
{
/// Creates a new [StreamBuilder] that builds itself based on the latest
/// snapshot of interaction with the specified
`stream`
and whose build
/// snapshot of interaction with the specified
[stream]
and whose build
/// strategy is given by [builder].
///
/// The [initialData] argument is used to create the initial snapshot. For
/// cases where there is no initial snapshot or the initial snapshot is not
/// yet available, callers may construct a [StreamBuilder] without an initial
/// snapshot using [new StreamBuilder.withoutInitialData].
/// The [initialData] is used to create the initial snapshot.
///
/// The [builder] must not be null.
const
StreamBuilder
({
Key
key
,
@required
T
initialData
,
Stream
<
T
>
stream
,
@required
this
.
builder
,
})
:
assert
(
builder
!=
null
),
hasInitialData
=
true
,
_initialData
=
initialData
,
super
(
key:
key
,
stream:
stream
);
/// Creates a new [StreamBuilder] that builds itself based on the latest
/// snapshot of interaction with the specified `stream` and whose build
/// strategy is given by [builder].
///
/// The initial snapshot will contain no data.
///
/// The [builder] must not be null.
const
StreamBuilder
.
withoutInitialData
({
Key
key
,
this
.
initialData
,
Stream
<
T
>
stream
,
@required
this
.
builder
,
})
:
assert
(
builder
!=
null
),
hasInitialData
=
false
,
_initialData
=
null
,
super
(
key:
key
,
stream:
stream
);
/// The build strategy currently used by this builder.
final
AsyncWidgetBuilder
<
T
>
builder
;
/// Whether this builder's initial snapshot contains data.
///
/// If this is false, then attempting to access [initialData] will throw an
/// error.
///
/// See also:
///
/// * [AsyncSnapshot.hasData], the corresponding property that will be set
/// in the initial snapshot.
final
bool
hasInitialData
;
/// The data that will be used to create the initial snapshot.
///
/// Providing this value (presumably obtained synchronously somehow when the
/// [Stream] was created) ensures that the first frame will show useful data.
/// Otherwise, the first frame will be built with a snapshot that contains no
/// data, regardless of whether a value is available on the stream: since
/// streams are asynchronous, no events from the stream can be obtained
/// before the initial build.
///
/// Some builders intentionally have no data when first built. For those
/// cases, callers can use the [new StreamBuilder.withoutInitialData]
/// constructor. When a builder was constructed in this way, attempting to
/// access the [initialData] property will throw a [StateError].
T
get
initialData
{
if
(!
hasInitialData
)
{
throw
StateError
(
'StreamBuilder was created without initial data, yet the initialData '
'property was accessed. If you wish your StreamBuilder to have initial '
'data, create it using the default constructor.'
,
);
}
return
_initialData
;
}
final
T
_initialData
;
/// Otherwise, the first frame will be built with the value null, regardless
/// of whether a value is available on the stream: since streams are
/// asynchronous, no events from the stream can be obtained before the initial
/// build.
final
T
initialData
;
@override
AsyncSnapshot
<
T
>
initial
()
{
return
hasInitialData
?
AsyncSnapshot
<
T
>.
withData
(
ConnectionState
.
none
,
initialData
)
:
AsyncSnapshot
<
T
>.
withoutData
(
ConnectionState
.
none
);
}
AsyncSnapshot
<
T
>
initial
()
=>
AsyncSnapshot
<
T
>.
withData
(
ConnectionState
.
none
,
initialData
);
@override
AsyncSnapshot
<
T
>
afterConnected
(
AsyncSnapshot
<
T
>
current
)
=>
current
.
inState
(
ConnectionState
.
waiting
);
@override
AsyncSnapshot
<
T
>
afterData
(
AsyncSnapshot
<
T
>
current
,
T
data
)
{
return
_TypeLiteral
.
isVoidType
(
T
)
?
AsyncSnapshot
<
T
>.
withoutData
(
ConnectionState
.
active
)
:
AsyncSnapshot
<
T
>.
withData
(
ConnectionState
.
active
,
data
);
return
AsyncSnapshot
<
T
>.
withData
(
ConnectionState
.
active
,
data
);
}
@override
...
...
@@ -557,17 +455,23 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
///
/// ## Builder contract
///
/// For a future that completes successfully with data, the [builder] will be
/// called with either both or only the latter of the following snapshots:
/// For a future that completes successfully with data, assuming [initialData]
/// is null, the [builder] will be called with either both or only the latter of
/// the following snapshots:
///
/// * `
AsyncSnapshot<String>.withoutData(ConnectionState.waiting
)`
/// * `AsyncSnapshot<String>.withData(ConnectionState.done, 'some data')`
/// * `
new AsyncSnapshot<String>.withData(ConnectionState.waiting, null
)`
/// * `
new
AsyncSnapshot<String>.withData(ConnectionState.done, 'some data')`
///
/// If that same future instead completed with an error, the [builder] would be
/// called with either both or only the latter of:
///
/// * `AsyncSnapshot<String>.withoutData(ConnectionState.waiting)`
/// * `AsyncSnapshot<String>.withError(ConnectionState.done, 'some error')`
/// * `new AsyncSnapshot<String>.withData(ConnectionState.waiting, null)`
/// * `new AsyncSnapshot<String>.withError(ConnectionState.done, 'some error')`
///
/// The initial snapshot data can be controlled by specifying [initialData]. You
/// would use this facility to ensure that if the [builder] is invoked before
/// the future completes, the snapshot carries data of your choice rather than
/// the default null value.
///
/// The data and error fields of the snapshot change only as the connection
/// state field transitions from `waiting` to `done`, and they will be retained
...
...
@@ -575,8 +479,8 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
/// old future has already completed successfully with data as above, changing
/// configuration to a new future results in snapshot pairs of the form:
///
/// * `AsyncSnapshot<String>.withData(ConnectionState.none, 'data of first future')`
/// * `AsyncSnapshot<String>.withData(ConnectionState.waiting, 'data of second future')`
/// * `
new
AsyncSnapshot<String>.withData(ConnectionState.none, 'data of first future')`
/// * `
new
AsyncSnapshot<String>.withData(ConnectionState.waiting, 'data of second future')`
///
/// In general, the latter will be produced only when the new future is
/// non-null, and the former only when the old future is non-null.
...
...
@@ -585,12 +489,6 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
/// `future?.asStream()`, except that snapshots with `ConnectionState.active`
/// may appear for the latter, depending on how the stream is implemented.
///
/// ## Void futures
///
/// The `FutureBuilder<void>` type will produce snapshots that contain no data:
///
/// * `AsyncSnapshot<String>.withoutData(ConnectionState.done)`
///
/// {@tool sample}
///
/// This sample shows a [FutureBuilder] configuring a text label to show the
...
...
@@ -599,13 +497,7 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
///
/// ```dart
/// FutureBuilder<String>(
/// // A previously-obtained `Future<String>` or null.
/// //
/// // This MUST NOT be created during the call to the `build()` method that
/// // creates the `FutureBuilder`. Doing so will cause a new future to be
/// // instantiated every time `build()` is called (potentially every frame).
/// future: _calculation,
///
/// future: _calculation, // a previously-obtained Future<String> or null
/// builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
/// switch (snapshot.connectionState) {
/// case ConnectionState.none:
...
...
@@ -628,33 +520,11 @@ class FutureBuilder<T> extends StatefulWidget {
/// Creates a widget that builds itself based on the latest snapshot of
/// interaction with a [Future].
///
/// The [future] argument must have been obtained earlier, e.g. during
/// [State.initState], [State.didUpdateConfig], or
/// [State.didChangeDependencies]. It must not be created during the
/// [State.build] or [StatelessWidget.build] method call when constructing
/// the [FutureBuilder]. If the [future] is created at the same time as the
/// [FutureBuilder], then every time the [FutureBuilder]'s parent is rebuilt,
/// the asynchronous task will be restarted.
///
// ignore: deprecated_member_use_from_same_package
/// The [initialData] argument specifies the data that will be used to create
/// the snapshots provided to [builder] until a non-null [future] has
/// completed. This argument is deprecated and will be removed in a future
/// stable release because snapshots that are provided to the [builder]
/// contain an [AsyncSnapshot.connectionState] property that indicates the
/// state of the [future]. The builder can use that connection state to
/// provide an "initial value" when the future has not yet completed.
///
/// The [builder] argument must not be null.
/// The [builder] must not be null.
const
FutureBuilder
({
Key
key
,
this
.
future
,
@Deprecated
(
'Instead of providing initialData to FutureBuilder, consider checking '
'for ConnectionState.none or ConnectionState.waiting in your build() '
'method to know whether the future has completed or not.'
,
)
this
.
initialData
,
// ignore: deprecated_member_use_from_same_package
this
.
initialData
,
@required
this
.
builder
,
})
:
assert
(
builder
!=
null
),
super
(
key:
key
);
...
...
@@ -663,10 +533,7 @@ class FutureBuilder<T> extends StatefulWidget {
/// possibly null.
///
/// If no future has yet completed, including in the case where [future] is
// ignore: deprecated_member_use_from_same_package
/// null, the snapshot provided to the [builder] will contain [initialData]
/// if this widget was created with initial data or will contain no data if
/// this widget was created without initial data.
/// null, the data provided to the [builder] will be set to [initialData].
final
Future
<
T
>
future
;
/// The build strategy currently used by this builder.
...
...
@@ -675,56 +542,29 @@ class FutureBuilder<T> extends StatefulWidget {
/// [AsyncSnapshot.connectionState] property will be one of the following
/// values:
///
/// * [ConnectionState.none]: [future] is null.
///
/// If this widget was created with initial data (deprecated), then the
/// [AsyncSnapshot.data] will be set to [initialData], unless a future has
/// previously completed, in which case the previous result persists.
///
/// If this widget was created without initial data, then the
/// [AsyncSnapshot.data] will be unset, and attempts to access the data
/// will result in an exception.
/// * [ConnectionState.none]: [future] is null. The [AsyncSnapshot.data] will
/// be set to [initialData], unless a future has previously completed, in
/// which case the previous result persists.
///
/// * [ConnectionState.waiting]: [future] is not null but has not yet
/// completed.
///
/// If this widget was created with initial data (deprecated), then the
/// [AsyncSnapshot.data] will be set to [initialData], unless a future has
/// previously completed, in which case the previous result persists.
///
/// If this widget was created without initial data, then the
/// [AsyncSnapshot.data] will be unset, and attempts to access the data
/// will result in an exception.
/// * [ConnectionState.waiting]: [future] is not null, but has not yet
/// completed. The [AsyncSnapshot.data] will be set to [initialData],
/// unless a future has previously completed, in which case the previous
/// result persists.
///
/// * [ConnectionState.done]: [future] is not null, and has completed. If the
/// future completed successfully, the [AsyncSnapshot.data] will be set to
/// the value to which the future completed. If it completed with an error,
/// [AsyncSnapshot.hasError] will be true and [AsyncSnapshot.error] will be
/// set to the error object.
///
/// In the case of [future] being a [Future<void>], the snapshot will not
/// contain data even if the future completed successfully.
final
AsyncWidgetBuilder
<
T
>
builder
;
/// The data that will be used to create the snapshots provided until a
/// non-null [future] has completed.
///
/// If the future completes with an error, the
[AsyncSnapshot] provided to
///
the [builder] will contain no data, regardless of [initialData]. (The
/// error itself will be available in [AsyncSnapshot.error], and
/// If the future completes with an error, the
data in the [AsyncSnapshot]
///
provided to the [builder] will become null, regardless of [initialData].
///
(The
error itself will be available in [AsyncSnapshot.error], and
/// [AsyncSnapshot.hasError] will be true.)
///
/// This field is deprecated and will be removed in a future stable release
/// because snapshots that are provided to the [builder] contain an
/// [AsyncSnapshot.connectionState] property that indicates the state of the
/// [future]. The builder can use that connection state to provide an
/// "initial value" when the future has not yet completed.
@Deprecated
(
'Instead of using FutureBuilder.initialData, consider checking '
'for ConnectionState.none or ConnectionState.waiting in your build() '
'ConnectionState.none or ConnectionState.waiting in your build() '
'method to know whether the future has completed or not.'
,
)
final
T
initialData
;
@override
...
...
@@ -742,11 +582,7 @@ class _FutureBuilderState<T> extends State<FutureBuilder<T>> {
@override
void
initState
()
{
super
.
initState
();
// ignore: deprecated_member_use_from_same_package
_snapshot
=
widget
.
initialData
==
null
?
AsyncSnapshot
<
T
>.
withoutData
(
ConnectionState
.
none
)
// ignore: deprecated_member_use_from_same_package
:
AsyncSnapshot
<
T
>.
withData
(
ConnectionState
.
none
,
widget
.
initialData
);
_snapshot
=
AsyncSnapshot
<
T
>.
withData
(
ConnectionState
.
none
,
widget
.
initialData
);
_subscribe
();
}
...
...
@@ -778,9 +614,7 @@ class _FutureBuilderState<T> extends State<FutureBuilder<T>> {
widget
.
future
.
then
<
void
>((
T
data
)
{
if
(
_activeCallbackIdentity
==
callbackIdentity
)
{
setState
(()
{
_snapshot
=
_TypeLiteral
.
isVoidType
(
T
)
?
AsyncSnapshot
<
T
>.
withoutData
(
ConnectionState
.
done
)
:
AsyncSnapshot
<
T
>.
withData
(
ConnectionState
.
done
,
data
);
_snapshot
=
AsyncSnapshot
<
T
>.
withData
(
ConnectionState
.
done
,
data
);
});
}
},
onError:
(
Object
error
)
{
...
...
@@ -798,33 +632,3 @@ class _FutureBuilderState<T> extends State<FutureBuilder<T>> {
_activeCallbackIdentity
=
null
;
}
}
/// Class that allows callers to reference instances of [Type] that would
/// otherwise not be valid expressions.
///
/// Generic types and the `void` type are not usable as Dart expressions, so
/// the following statements are not legal and all yield compile-time errors:
///
/// ```dart
/// if (type == List<int>) print('msg');
/// if (type == void) print('msg');
/// Type type = List<int>;
/// ```
///
/// This class allows callers to get handles on such types, like so:
///
/// ```dart
/// if (type == const _TypeLiteral<List<int>>().type) print('msg');
/// if (type == const _TypeLiteral<void>().type) print('msg');
/// Type type = const _TypeLiteral<List<int>>().type;
/// ```
class
_TypeLiteral
<
T
>
{
/// Creates a new [_TypeLiteral].
const
_TypeLiteral
();
/// Returns whether the specified type represents a "void" type.
static
bool
isVoidType
(
Type
type
)
=>
type
==
const
_TypeLiteral
<
void
>().
type
;
/// The [Type] (`T`) represented by this [_TypeLiteral].
Type
get
type
=>
T
;
}
packages/flutter/test/widgets/async_test.dart
View file @
fbd4bb91
...
...
@@ -12,33 +12,23 @@ void main() {
return
Text
(
snapshot
.
toString
(),
textDirection:
TextDirection
.
ltr
);
}
group
(
'AsyncSnapshot'
,
()
{
test
(
'data succeeds if data is present'
,
()
{
const
AsyncSnapshot
<
String
>
snapshot
=
AsyncSnapshot
<
String
>.
withData
(
ConnectionState
.
done
,
'hello'
);
expect
(
snapshot
.
hasData
,
isTrue
);
expect
(
snapshot
.
data
,
'hello'
);
expect
(
snapshot
.
hasError
,
isFalse
);
expect
(
snapshot
.
error
,
isNull
);
});
test
(
'data throws if there is an error'
,
()
{
const
AsyncSnapshot
<
String
>
snapshot
=
AsyncSnapshot
<
String
>.
withError
(
ConnectionState
.
done
,
'error'
);
expect
(
snapshot
.
hasData
,
isFalse
);
expect
(()
=>
snapshot
.
data
,
throwsA
(
equals
(
'error'
)));
expect
(
snapshot
.
hasError
,
isTrue
);
expect
(
snapshot
.
error
,
'error'
);
test
(
'requiring data succeeds if data is present'
,
()
{
expect
(
const
AsyncSnapshot
<
String
>.
withData
(
ConnectionState
.
done
,
'hello'
).
requireData
,
'hello'
,
);
});
test
(
'data throws if created without data'
,
()
{
const
AsyncSnapshot
<
String
>
snapshot
=
AsyncSnapshot
<
String
>.
withoutData
(
ConnectionState
.
none
);
expect
(
snapshot
.
hasData
,
isFalse
);
expect
(()
=>
snapshot
.
data
,
throwsStateError
);
expect
(
snapshot
.
hasError
,
isFalse
);
expect
(
snapshot
.
error
,
isNull
);
test
(
'requiring data fails if there is an error'
,
()
{
expect
(
()
=>
const
AsyncSnapshot
<
String
>.
withError
(
ConnectionState
.
done
,
'error'
).
requireData
,
throwsA
(
equals
(
'error'
)),
);
});
test
(
'data can be null'
,
()
{
const
AsyncSnapshot
<
int
>
snapshot
=
AsyncSnapshot
<
int
>.
withData
(
ConnectionState
.
none
,
null
);
expect
(
snapshot
.
hasData
,
isTrue
);
expect
(
snapshot
.
data
,
isNull
);
expect
(
snapshot
.
hasError
,
isFalse
);
expect
(
snapshot
.
error
,
isNull
);
test
(
'requiring data fails if snapshot has neither data nor error'
,
()
{
expect
(
()
=>
const
AsyncSnapshot
<
String
>.
nothing
().
requireData
,
throwsStateError
,
);
});
});
group
(
'Async smoke tests'
,
()
{
...
...
@@ -50,7 +40,7 @@ void main() {
await
eventFiring
(
tester
);
});
testWidgets
(
'StreamBuilder'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>
.
withoutInitialData
(
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>(
stream:
Stream
<
String
>.
fromIterable
(<
String
>[
'hello'
,
'world'
]),
builder:
snapshotText
,
));
...
...
@@ -69,12 +59,12 @@ void main() {
await
tester
.
pumpWidget
(
FutureBuilder
<
String
>(
key:
key
,
future:
null
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none
, null, null
)'
),
findsOneWidget
);
final
Completer
<
String
>
completer
=
Completer
<
String
>();
await
tester
.
pumpWidget
(
FutureBuilder
<
String
>(
key:
key
,
future:
completer
.
future
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsOneWidget
);
});
testWidgets
(
'gracefully handles transition to null future'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
...
...
@@ -82,14 +72,14 @@ void main() {
await
tester
.
pumpWidget
(
FutureBuilder
<
String
>(
key:
key
,
future:
completer
.
future
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsOneWidget
);
await
tester
.
pumpWidget
(
FutureBuilder
<
String
>(
key:
key
,
future:
null
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none
, null, null
)'
),
findsOneWidget
);
completer
.
complete
(
'hello'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none
, null, null
)'
),
findsOneWidget
);
});
testWidgets
(
'gracefully handles transition to other future'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
...
...
@@ -98,132 +88,125 @@ void main() {
await
tester
.
pumpWidget
(
FutureBuilder
<
String
>(
key:
key
,
future:
completerA
.
future
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsOneWidget
);
await
tester
.
pumpWidget
(
FutureBuilder
<
String
>(
key:
key
,
future:
completerB
.
future
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsOneWidget
);
completerB
.
complete
(
'B'
);
completerA
.
complete
(
'A'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
data: B
)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
B, null
)'
),
findsOneWidget
);
});
testWidgets
(
'tracks life-cycle of Future to success'
,
(
WidgetTester
tester
)
async
{
final
Completer
<
String
>
completer
=
Completer
<
String
>();
await
tester
.
pumpWidget
(
FutureBuilder
<
String
>(
future:
completer
.
future
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsOneWidget
);
completer
.
complete
(
'hello'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
data: hello
)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
hello, null
)'
),
findsOneWidget
);
});
testWidgets
(
'tracks life-cycle of Future to error'
,
(
WidgetTester
tester
)
async
{
final
Completer
<
String
>
completer
=
Completer
<
String
>();
await
tester
.
pumpWidget
(
FutureBuilder
<
String
>(
future:
completer
.
future
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsOneWidget
);
completer
.
completeError
(
'bad'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
error:
bad)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
null,
bad)'
),
findsOneWidget
);
});
testWidgets
(
'
produces snapshot with null data for null-completing data Future
'
,
(
WidgetTester
tester
)
async
{
final
Completer
<
String
>
completer
=
Completer
<
String
>
();
testWidgets
(
'
runs the builder using given initial data
'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
await
tester
.
pumpWidget
(
FutureBuilder
<
String
>(
future:
completer
.
future
,
builder:
snapshotText
,
key:
key
,
future:
null
,
builder:
snapshotText
,
initialData:
'I'
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
completer
.
complete
(
null
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done, data: null)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none, I, null)'
),
findsOneWidget
);
});
testWidgets
(
'produces snapshot with null data for Future<Null>'
,
(
WidgetTester
tester
)
async
{
final
Completer
<
Null
>
completer
=
Completer
<
Null
>();
// ignore: prefer_void_to_null
await
tester
.
pumpWidget
(
FutureBuilder
<
Null
>(
// ignore: prefer_void_to_null
future:
completer
.
future
,
builder:
snapshotText
,
testWidgets
(
'ignores initialData when reconfiguring'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
await
tester
.
pumpWidget
(
FutureBuilder
<
String
>(
key:
key
,
future:
null
,
builder:
snapshotText
,
initialData:
'I'
,
));
expect
(
find
.
text
(
'AsyncSnapshot<Null>(ConnectionState.waiting)'
),
findsOneWidget
);
completer
.
complete
();
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<Null>(ConnectionState.done, data: null)'
),
findsOneWidget
);
});
testWidgets
(
'produces snapshot with no data for Future<void>'
,
(
WidgetTester
tester
)
async
{
final
Completer
<
void
>
completer
=
Completer
<
void
>();
await
tester
.
pumpWidget
(
FutureBuilder
<
void
>(
future:
completer
.
future
,
builder:
(
BuildContext
context
,
AsyncSnapshot
<
void
>
snapshot
)
{
return
Text
(
snapshot
.
toString
(),
textDirection:
TextDirection
.
ltr
);
},
),
);
expect
(
find
.
text
(
'AsyncSnapshot<void>(ConnectionState.waiting)'
),
findsOneWidget
);
completer
.
complete
();
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<void>(ConnectionState.done)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none, I, null)'
),
findsOneWidget
);
final
Completer
<
String
>
completer
=
Completer
<
String
>();
await
tester
.
pumpWidget
(
FutureBuilder
<
String
>(
key:
key
,
future:
completer
.
future
,
builder:
snapshotText
,
initialData:
'Ignored'
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting, I, null)'
),
findsOneWidget
);
});
});
group
(
'StreamBuilder'
,
()
{
testWidgets
(
'gracefully handles transition from null stream'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>
.
withoutInitialData
(
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>(
key:
key
,
stream:
null
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none
, null, null
)'
),
findsOneWidget
);
final
StreamController
<
String
>
controller
=
StreamController
<
String
>();
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>
.
withoutInitialData
(
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>(
key:
key
,
stream:
controller
.
stream
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsOneWidget
);
});
testWidgets
(
'gracefully handles transition to null stream'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
final
StreamController
<
String
>
controller
=
StreamController
<
String
>();
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>
.
withoutInitialData
(
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>(
key:
key
,
stream:
controller
.
stream
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>
.
withoutInitialData
(
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsOneWidget
);
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>(
key:
key
,
stream:
null
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none
, null, null
)'
),
findsOneWidget
);
});
testWidgets
(
'gracefully handles transition to other stream'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
final
StreamController
<
String
>
controllerA
=
StreamController
<
String
>();
final
StreamController
<
String
>
controllerB
=
StreamController
<
String
>();
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>
.
withoutInitialData
(
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>(
key:
key
,
stream:
controllerA
.
stream
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>
.
withoutInitialData
(
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsOneWidget
);
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>(
key:
key
,
stream:
controllerB
.
stream
,
builder:
snapshotText
,
));
controllerB
.
add
(
'B'
);
controllerA
.
add
(
'A'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.active,
data: B
)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.active,
B, null
)'
),
findsOneWidget
);
});
testWidgets
(
'tracks events and errors of stream until completion'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
final
StreamController
<
String
>
controller
=
StreamController
<
String
>();
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>
.
withoutInitialData
(
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>(
key:
key
,
stream:
controller
.
stream
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsOneWidget
);
controller
.
add
(
'1'
);
controller
.
add
(
'2'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.active,
data: 2
)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.active,
2, null
)'
),
findsOneWidget
);
controller
.
add
(
'3'
);
controller
.
addError
(
'bad'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.active,
error:
bad)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.active,
null,
bad)'
),
findsOneWidget
);
controller
.
add
(
'4'
);
controller
.
close
();
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
data: 4
)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
4, null
)'
),
findsOneWidget
);
});
testWidgets
(
'runs the builder using given initial data'
,
(
WidgetTester
tester
)
async
{
final
StreamController
<
String
>
controller
=
StreamController
<
String
>();
...
...
@@ -232,7 +215,7 @@ void main() {
builder:
snapshotText
,
initialData:
'I'
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting,
data: I
)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting,
I, null
)'
),
findsOneWidget
);
});
testWidgets
(
'ignores initialData when reconfiguring'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
...
...
@@ -242,7 +225,7 @@ void main() {
builder:
snapshotText
,
initialData:
'I'
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none,
data: I
)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none,
I, null
)'
),
findsOneWidget
);
final
StreamController
<
String
>
controller
=
StreamController
<
String
>();
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>(
key:
key
,
...
...
@@ -250,69 +233,7 @@ void main() {
builder:
snapshotText
,
initialData:
'Ignored'
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting, data: I)'
),
findsOneWidget
);
});
testWidgets
(
'produces snapshots with null data for null-producing stream'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
final
StreamController
<
String
>
controller
=
StreamController
<
String
>();
await
tester
.
pumpWidget
(
StreamBuilder
<
String
>.
withoutInitialData
(
key:
key
,
stream:
controller
.
stream
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsOneWidget
);
controller
.
add
(
null
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.active, data: null)'
),
findsOneWidget
);
controller
.
addError
(
'bad'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.active, error: bad)'
),
findsOneWidget
);
controller
.
add
(
null
);
controller
.
close
();
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done, data: null)'
),
findsOneWidget
);
});
testWidgets
(
'produces snapshots with null data for Stream<Null>'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
final
StreamController
<
Null
>
controller
=
StreamController
<
Null
>();
// ignore: prefer_void_to_null
await
tester
.
pumpWidget
(
StreamBuilder
<
Null
>.
withoutInitialData
(
// ignore: prefer_void_to_null
key:
key
,
stream:
controller
.
stream
,
builder:
snapshotText
,
));
expect
(
find
.
text
(
'AsyncSnapshot<Null>(ConnectionState.waiting)'
),
findsOneWidget
);
controller
.
add
(
null
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<Null>(ConnectionState.active, data: null)'
),
findsOneWidget
);
controller
.
addError
(
'bad'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<Null>(ConnectionState.active, error: bad)'
),
findsOneWidget
);
controller
.
add
(
null
);
controller
.
close
();
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<Null>(ConnectionState.done, data: null)'
),
findsOneWidget
);
});
testWidgets
(
'produces snapshots with no data for Stream<void>'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
key
=
GlobalKey
();
final
StreamController
<
void
>
controller
=
StreamController
<
void
>();
await
tester
.
pumpWidget
(
StreamBuilder
<
void
>.
withoutInitialData
(
key:
key
,
stream:
controller
.
stream
,
builder:
(
BuildContext
context
,
AsyncSnapshot
<
void
>
snapshot
)
{
return
Text
(
snapshot
.
toString
(),
textDirection:
TextDirection
.
ltr
);
},
));
expect
(
find
.
text
(
'AsyncSnapshot<void>(ConnectionState.waiting)'
),
findsOneWidget
);
controller
.
add
(
null
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<void>(ConnectionState.active)'
),
findsOneWidget
);
controller
.
addError
(
'bad'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<void>(ConnectionState.active, error: bad)'
),
findsOneWidget
);
controller
.
add
(
null
);
controller
.
close
();
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<void>(ConnectionState.done)'
),
findsOneWidget
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting, I, null)'
),
findsOneWidget
);
});
});
group
(
'FutureBuilder and StreamBuilder behave identically on Stream from Future'
,
()
{
...
...
@@ -320,30 +241,48 @@ void main() {
final
Completer
<
String
>
completer
=
Completer
<
String
>();
await
tester
.
pumpWidget
(
Column
(
children:
<
Widget
>[
FutureBuilder
<
String
>(
future:
completer
.
future
,
builder:
snapshotText
),
StreamBuilder
<
String
>
.
withoutInitialData
(
stream:
completer
.
future
.
asStream
(),
builder:
snapshotText
),
StreamBuilder
<
String
>(
stream:
completer
.
future
.
asStream
(),
builder:
snapshotText
),
]));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsNWidgets
(
2
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsNWidgets
(
2
));
completer
.
complete
(
'hello'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
data: hello
)'
),
findsNWidgets
(
2
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
hello, null
)'
),
findsNWidgets
(
2
));
});
testWidgets
(
'when completing with error'
,
(
WidgetTester
tester
)
async
{
final
Completer
<
String
>
completer
=
Completer
<
String
>();
await
tester
.
pumpWidget
(
Column
(
children:
<
Widget
>[
FutureBuilder
<
String
>(
future:
completer
.
future
,
builder:
snapshotText
),
StreamBuilder
<
String
>
.
withoutInitialData
(
stream:
completer
.
future
.
asStream
(),
builder:
snapshotText
),
StreamBuilder
<
String
>(
stream:
completer
.
future
.
asStream
(),
builder:
snapshotText
),
]));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting)'
),
findsNWidgets
(
2
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting
, null, null
)'
),
findsNWidgets
(
2
));
completer
.
completeError
(
'bad'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
error:
bad)'
),
findsNWidgets
(
2
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done,
null,
bad)'
),
findsNWidgets
(
2
));
});
testWidgets
(
'when Future is null'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Column
(
children:
<
Widget
>[
FutureBuilder
<
String
>(
future:
null
,
builder:
snapshotText
),
StreamBuilder
<
String
>
.
withoutInitialData
(
stream:
null
,
builder:
snapshotText
),
StreamBuilder
<
String
>(
stream:
null
,
builder:
snapshotText
),
]));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none)'
),
findsNWidgets
(
2
));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none, null, null)'
),
findsNWidgets
(
2
));
});
testWidgets
(
'when initialData is used with null Future and Stream'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Column
(
children:
<
Widget
>[
FutureBuilder
<
String
>(
future:
null
,
builder:
snapshotText
,
initialData:
'I'
),
StreamBuilder
<
String
>(
stream:
null
,
builder:
snapshotText
,
initialData:
'I'
),
]));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.none, I, null)'
),
findsNWidgets
(
2
));
});
testWidgets
(
'when using initialData and completing with data'
,
(
WidgetTester
tester
)
async
{
final
Completer
<
String
>
completer
=
Completer
<
String
>();
await
tester
.
pumpWidget
(
Column
(
children:
<
Widget
>[
FutureBuilder
<
String
>(
future:
completer
.
future
,
builder:
snapshotText
,
initialData:
'I'
),
StreamBuilder
<
String
>(
stream:
completer
.
future
.
asStream
(),
builder:
snapshotText
,
initialData:
'I'
),
]));
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.waiting, I, null)'
),
findsNWidgets
(
2
));
completer
.
complete
(
'hello'
);
await
eventFiring
(
tester
);
expect
(
find
.
text
(
'AsyncSnapshot<String>(ConnectionState.done, hello, null)'
),
findsNWidgets
(
2
));
});
});
group
(
'StreamBuilderBase'
,
()
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment