Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter and Dart and Future. Is this a known feature of Dart Futures and null?

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";
    });
  }

like image 264
Brian Oh Avatar asked Jan 22 '26 12:01

Brian Oh


1 Answers

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.

like image 64
jamesdlin Avatar answered Jan 27 '26 02:01

jamesdlin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!