Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 35 additions & 21 deletions packages/flutter/lib/src/widgets/async.dart
Original file line number Diff line number Diff line change
Expand Up @@ -369,12 +369,15 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
/// The data and error fields of snapshots produced are only changed when the
/// state is `ConnectionState.active`.
///
/// 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.
/// The initial snapshot data can be controlled by specifying [initialData] in
/// the [new StreamBuilder.withInitialData] constructor. This can be used to
/// ensure that the first frame has an 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]
/// constructor may be used. Doing so will cause the first frame to have a
/// snapshot that contains no data:
///
/// * `AsyncSnapshot<int>.withoutData(ConnectionState.waiting)`
///
/// ## Void StreamBuilders
///
Expand All @@ -394,7 +397,7 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
/// set by a selector elsewhere in the UI.
///
/// ```dart
/// StreamBuilder<int>(
/// StreamBuilder<int>.withInitialData(
/// stream: _lot?.bids, // a Stream<int> or null
/// initialData: 100, // initial seed value
/// builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
Expand Down Expand Up @@ -424,36 +427,46 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
/// 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].
// ignore: deprecated_member_use_from_same_package
/// The [initialData] argument is deprecated and will be removed in a future
/// release. To specify initial data to a [StreamBuilder], use the
/// [new StreamBuilder.withInitialData] constructor instead. While this
/// argument still exists, a null value will be interpreted as "no initial
/// data".
///
/// The [builder] must not be null.
const StreamBuilder({
Key key,
@required T initialData,
@Deprecated(
'If you wish to provide initialData to StreamBuilder, you should use '
'the [new StreamBuilder.withInitialData] constructor instead.',
)
T initialData,
Stream<T> stream,
@required this.builder,
}) : assert(builder != null),
hasInitialData = true,
_initialData = initialData,
hasInitialData = initialData != null, // ignore: deprecated_member_use_from_same_package
_initialData = initialData, // ignore: deprecated_member_use_from_same_package
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 [initialData] argument (possibly null) 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].
///
/// The [builder] must not be null.
const StreamBuilder.withoutInitialData({
const StreamBuilder.withInitialData({
Key key,
@required T initialData,
Stream<T> stream,
@required this.builder,
}) : assert(builder != null),
hasInitialData = false,
_initialData = null,
hasInitialData = true,
_initialData = initialData,
super(key: key, stream: stream);

/// The build strategy currently used by this builder.
Expand All @@ -479,9 +492,10 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
/// 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
/// This is set by callers using the [new StreamBuilder.withInitialData]
/// constructor. [StreamBuilder]s that intentionally have no data when first
/// built are constructed using the [new StreamBuilder] constructor. When a
/// [StreamBuilder] was constructed without initial data, attempting to
/// access the [initialData] property will throw a [StateError].
T get initialData {
if (!hasInitialData) {
Expand Down
34 changes: 17 additions & 17 deletions packages/flutter/test/widgets/async_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,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,
));
Expand Down Expand Up @@ -167,24 +167,24 @@ void main() {
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);
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);
});
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(
await tester.pumpWidget(StreamBuilder<String>(
key: key, stream: null, builder: snapshotText,
));
expect(find.text('AsyncSnapshot<String>(ConnectionState.none)'), findsOneWidget);
Expand All @@ -193,11 +193,11 @@ void main() {
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(
await tester.pumpWidget(StreamBuilder<String>(
key: key, stream: controllerB.stream, builder: snapshotText,
));
controllerB.add('B');
Expand All @@ -208,7 +208,7 @@ void main() {
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);
Expand All @@ -227,7 +227,7 @@ void main() {
});
testWidgets('runs the builder using given initial data', (WidgetTester tester) async {
final StreamController<String> controller = StreamController<String>();
await tester.pumpWidget(StreamBuilder<String>(
await tester.pumpWidget(StreamBuilder<String>.withInitialData(
stream: controller.stream,
builder: snapshotText,
initialData: 'I',
Expand All @@ -236,15 +236,15 @@ void main() {
});
testWidgets('ignores initialData when reconfiguring', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
await tester.pumpWidget(StreamBuilder<String>(
await tester.pumpWidget(StreamBuilder<String>.withInitialData(
key: key,
stream: null,
builder: snapshotText,
initialData: 'I',
));
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, data: I)'), findsOneWidget);
final StreamController<String> controller = StreamController<String>();
await tester.pumpWidget(StreamBuilder<String>(
await tester.pumpWidget(StreamBuilder<String>.withInitialData(
key: key,
stream: controller.stream,
builder: snapshotText,
Expand All @@ -255,7 +255,7 @@ void main() {
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(
await tester.pumpWidget(StreamBuilder<String>(
key: key,
stream: controller.stream,
builder: snapshotText,
Expand All @@ -275,7 +275,7 @@ void main() {
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
await tester.pumpWidget(StreamBuilder<Null>( // ignore: prefer_void_to_null
key: key,
stream: controller.stream,
builder: snapshotText,
Expand All @@ -295,7 +295,7 @@ void main() {
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(
await tester.pumpWidget(StreamBuilder<void>(
key: key,
stream: controller.stream,
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
Expand All @@ -320,7 +320,7 @@ 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));
completer.complete('hello');
Expand All @@ -331,7 +331,7 @@ 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));
completer.completeError('bad');
Expand All @@ -341,7 +341,7 @@ void main() {
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));
});
Expand Down