Revert "AsyncSnapshot.data to throw if error or no data (#34626)" (#36618)

This reverts commit b61fcfd25d0b32ec3791d09493d0235bb9a2b43f.
diff --git a/packages/flutter/lib/src/widgets/async.dart b/packages/flutter/lib/src/widgets/async.dart
index 18844f5..7491908 100644
--- a/packages/flutter/lib/src/widgets/async.dart
+++ b/packages/flutter/lib/src/widgets/async.dart
@@ -59,41 +59,35 @@
   ///
   /// 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 @@
 /// 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 @@
 ///    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;
+  /// 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;
 
-  /// The latest data received by the asynchronous computation, failing if
-  /// there is no data.
+  /// Returns latest data received, failing if there is no data.
   ///
-  /// If [hasData] is true, accessing this property will not throw an error.
-  ///
-  /// If [error] is not null, attempting to access this property will throw
-  /// [error]. See [hasError].
-  ///
-  /// If neither [hasData] nor [hasError] is true, then accessing this
-  /// property will throw a [StateError].
-  T get data {
+  /// Throws [error], if [hasError]. Throws [StateError], if neither [hasData]
+  /// nor [hasError].
+  T get requireData {
     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 @@
 
   /// 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 @@
 /// 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 @@
 ///
 /// 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 @@
 /// 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 @@
 /// ```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 @@
 // 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,
+    this.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,
-    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 @@
 ///
 /// ## 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 @@
 /// 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 @@
 /// `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 @@
 ///
 /// ```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 @@
   /// 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 @@
   /// 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 @@
   /// [AsyncSnapshot.connectionState] property will be one of the following
   /// values:
   ///
-  ///  * [ConnectionState.none]: [future] is null.
+  ///  * [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.
   ///
-  ///    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.
-  ///
-  ///    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 @@
   @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 @@
       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 @@
     _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;
-}
diff --git a/packages/flutter/test/widgets/async_test.dart b/packages/flutter/test/widgets/async_test.dart
index 6e559af..d8af97a 100644
--- a/packages/flutter/test/widgets/async_test.dart
+++ b/packages/flutter/test/widgets/async_test.dart
@@ -12,33 +12,23 @@
     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('requiring data succeeds if data is present', () {
+      expect(
+        const AsyncSnapshot<String>.withData(ConnectionState.done, 'hello').requireData,
+        'hello',
+      );
     });
-    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 fails if there is an error', () {
+      expect(
+        () => const AsyncSnapshot<String>.withError(ConnectionState.done, 'error').requireData,
+        throwsA(equals('error')),
+      );
     });
-    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('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 @@
       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 @@
       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 @@
       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 @@
       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 {
+    testWidgets('runs the builder using given initial data', (WidgetTester tester) async {
+      final GlobalKey key = GlobalKey();
+      await tester.pumpWidget(FutureBuilder<String>(
+        key: key,
+        future: null,
+        builder: snapshotText,
+        initialData: 'I',
+      ));
+      expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsOneWidget);
+    });
+    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<String>(ConnectionState.none, I, null)'), findsOneWidget);
       final Completer<String> completer = Completer<String>();
       await tester.pumpWidget(FutureBuilder<String>(
-        future: completer.future, builder: snapshotText,
+        key: key,
+        future: completer.future,
+        builder: snapshotText,
+        initialData: 'Ignored',
       ));
-      expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting)'), findsOneWidget);
-      completer.complete(null);
-      await eventFiring(tester);
-      expect(find.text('AsyncSnapshot<String>(ConnectionState.done, data: 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,
-      ));
-      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.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 @@
         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 @@
         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 @@
         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 @@
       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', () {