Reputation: 10750
In the code below (Dart 2.4.0 Windows), returning null from a future that indicates a return type of String results in:
I/flutter ( 8735): The following assertion was thrown building Builder:
I/flutter ( 8735): type 'Future<dynamic>' is not a subtype of type 'Future<String>'
If I return "null as String;", VSCode shows a warning "unnecessary cast", however, the code works and I don't get the fatal error. The Code in question is below. I have commented out the line that was causing the problem and now return "null as String". I use a null String all the time in Dart without a problem, so it only appears to be a problem with Futures AFAIK. Is this just a known fact about Dart and Futures and this is the workaround ("return null as String")?
@override
void initState() {
super.initState();
Future<String> future = _initPreferences();
future.then((sError) {
if (sError != null) debugPrint("\n*** Error from initPrefs = $sError\n");
if (sError == null) debugPrint("\n***Preferences were initialized OK\n");
});
}
Future<String> _initPreferences() {
return SharedPreferences.getInstance().then((prefs) {
_prefs = prefs;
//return null;
return null as String;
}).catchError((vError) {
return "Unable to load categories";
});
}
Upvotes: 3
Views: 1391
Reputation: 90135
You didn't specify a type when using Future.then
, so it has to be inferred. Since you return null
, it doesn't know what type it's supposed to be, so your call to Future.then
does not return a Future<String>
.
You can help the inference engine along by explicitly specifying the type via then<String>(...)
:
Future<String> _initPreferences() {
return SharedPreferences.getInstance().then<String>((prefs) {
_prefs = prefs;
return null;
}).catchError((vError) {
return "Unable to load categories";
});
}
although even better would be to just use async
and await
:
Future<String> _initPreferences() async {
try {
final prefs = await SharedPreferences.getInstance();
_prefs = prefs;
return null;
} catch (vError) {
return "Unable to load categories";
}
}
Here is a simpler reproduction case:
Future<String> foo() {
final future = Future(() => null);
print('${future.runtimeType}');
return future;
}
Future<void> main() async {
await foo();
}
I would expect future
to be a Future<Null>
, but it ends up being a Future<dynamic>
instead, and I'm not sure why. (Null
is special and allows Future<Null>
to be assigned to Future<T>
.) I suspect it might be a bug since dartanalyzer
does not generate any warnings about it. (And contrary to what some comments say, this does not seem to be new to Dart 2.4.0; I tried with Dart 2.3.0, 2.3.2, and 2.4.0.) I've filed an issue about it.
Upvotes: 1
Reputation: 10750
Someone did post what appears to be the correct answer, but then it somehow disappeared, so I can't take credit. The whole point of the exercise is that I want to use a Future (not await) The answer was/is: (then)
Future<String> _initPreferences() {
return SharedPreferences.getInstance().then<String>((prefs)
Upvotes: 0