I have some problems understanding the StreamBuilder using the new null-aware operators.
As a learning project I am implementing a sign-in flow with the BloC pattern. For my email sign-in form I have created a model class which I access through a StreamBuilder. Without the use of initialData it makes complete sense that snapshot.data can be null. However, setting the initialData to a predefined empty model, snapshot.data could never be null right? Here is my code snippet:
@override
Widget build(BuildContext context) {
return StreamBuilder<EmailSignInModel>(
stream: widget.bloc.modelStream,
initialData: EmailSignInModel(),
builder: (context, snapshot) {
final EmailSignInModel model = snapshot.data;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: _buildChildren(),
),
);
});
}
The compiler warns me that snapshot.data is of type <EmailSignModel?> which is not equal to . I could fix this with snapshot.data ?? EmailSignModel()
but this would be redundant with initialData right?
What is the proper way of handling this situation and taken care of the null-awareness of Dart?
Diving into the source code, I found the following:
https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/async.dart
/// The latest data received by the asynchronous computation.
///
/// If this is non-null, [hasData] will be true.
///
/// If [error] is not null, this will be null. See [hasError].
///
/// If the asynchronous computation has never returned a value, this may be
/// set to an initial data value specified by the relevant widget. See
/// [FutureBuilder.initialData] and [StreamBuilder.initialData].
final T? data;
/// Returns latest data received, failing if there is no data.
///
/// Throws [error], if [hasError]. Throws [StateError], if neither [hasData]
/// nor [hasError].
T get requireData {
if (hasData)
return data!;
if (hasError)
throw error!;
throw StateError('Snapshot has neither data nor error');
}
AsyncSnapshot
actually has a requireData
getter, which will ensure non-null or will throw an error. So just replace snapshot.data
with snapshot.requireData
This still requires some manual work where the use of initialData
and requireData
need to be kept in sync. You could also just use snapshot.data!
which basically does the same.