Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Confusing error message: type '_Future' is not a subtype of type 'Future<bool>' #15654

Closed
lovasoa opened this issue Mar 17, 2018 · 16 comments
Closed

Comments

@lovasoa
Copy link

lovasoa commented Mar 17, 2018

Steps to Reproduce

Calling FlutterBlue.instance.isOn from the flutterBlue library.

Logs

I/Choreographer(25521): Skipped 206 frames!  The application may be doing too much work on its main thread.
I/flutter (25521): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (25521): The following assertion was thrown attaching to the render tree:
I/flutter (25521): type '_Future' is not a subtype of type 'Future<bool>' where
I/flutter (25521):   _Future is from dart:async
I/flutter (25521):   Future is from dart:async
I/flutter (25521):   bool is from dart:core
I/flutter (25521): 
I/flutter (25521): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter (25521): more information in this error message to help you determine and fix the underlying cause.
I/flutter (25521): In either case, please report this assertion by filing a bug on GitHub:
I/flutter (25521):   https://github.com/flutter/flutter/issues/new
I/flutter (25521): 
I/flutter (25521): When the exception was thrown, this was the stack:
I/flutter (25521): #0      FlutterBlue.isOn (file:///home/olojkine/.pub-cache/hosted/pub.dartlang.org/flutter_blue-0.3.1/lib/src/flutter_blue.dart:36:37)
I/flutter (25521): #1      new _GeovilMainState (file:///home/olojkine/Bureau/geovil_demo/lib/main.dart:30:17)
I/flutter (25521): #2      GeovilMain.createState (file:///home/olojkine/Bureau/geovil_demo/lib/main.dart:20:41)
I/flutter (25521): #3      new StatefulElement (package:flutter/src/widgets/framework.dart:3693:23)
I/flutter (25521): #4      StatefulWidget.createElement (package:flutter/src/widgets/framework.dart:781:42)
I/flutter (25521): #5      Element.inflateWidget (package:flutter/src/widgets/framework.dart:2888:40)
I/flutter (25521): #6      Element.updateChild (package:flutter/src/widgets/framework.dart:2693:12)
I/flutter (25521): #7      RenderObjectToWidgetElement._rebuild (package:flutter/src/widgets/binding.dart:852:16)
I/flutter (25521): #8      RenderObjectToWidgetElement.mount (package:flutter/src/widgets/binding.dart:823:5)
I/flutter (25521): #9      RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure> (package:flutter/src/widgets/binding.dart:769:17)
I/flutter (25521): #10     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2205:19)
I/flutter (25521): #11     RenderObjectToWidgetAdapter.attachToRenderTree (package:flutter/src/widgets/binding.dart:768:13)
I/flutter (25521): #12     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding&WidgetsBinding.attachRootWidget (package:flutter/src/widgets/binding.dart:657:7)
I/flutter (25521): #13     runApp (package:flutter/src/widgets/binding.dart:699:7)
I/flutter (25521): #14     main (file:///home/olojkine/Bureau/geovil_demo/lib/main.dart:14:16)
I/flutter (25521): #15     _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:279:19)
I/flutter (25521): #16     _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:165:12)
I/flutter (25521): ════════════════════════════════════════════════════════════════════════════════════════════════════
F/libc    (25521): Fatal signal 11 (SIGSEGV), code 128, fault addr 0x0 in tid 25562 (ui_thread)

Flutter Doctor

$ flutter doctor -v
[✓] Flutter (Channel master, v0.2.3-pre.37, on Linux, locale fr_FR.UTF-8)
    • Flutter version 0.2.3-pre.37 at /home/olojkine/Bureau/flutter
    • Framework revision 0f3eada066 (il y a 2 heures), 2018-03-17 08:11:52 -0700
    • Engine revision 464af48eb2
    • Dart version 2.0.0-dev.37.0.flutter-7328726088

[✓] Android toolchain - develop for Android devices (Android SDK 27.0.3)
    • Android SDK at /home/olojkine/Android/Sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-27, build-tools 27.0.3
    • Java binary at: /home/olojkine/Téléchargements/android-studio/jre/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-915-b01)
    • All Android licenses accepted.

[✓] Android Studio (version 3.0)
    • Android Studio at /home/olojkine/Téléchargements/android-studio
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-915-b01)

[✓] Connected devices (1 available)
    • XT1039 • TA00301VQ0 • android-arm • Android 7.1.2 (API 25)

• No issues found!
@xqwzts
Copy link
Contributor

xqwzts commented Mar 17, 2018

Hi @lovasoa could you share a more detailed snippet of the code that is causing this?

@lovasoa
Copy link
Author

lovasoa commented Mar 17, 2018

I have a class that extends State<T>. In its constructor, I have:

    flutterBlue.isOn.then(_setBluetoothState);

And the signature of _setBluetoothState is

  void _setBluetoothState(bool isOn) {

.

The variable flutterBlue is global and defined as

FlutterBlue flutterBlue = FlutterBlue.instance;

@lovasoa
Copy link
Author

lovasoa commented Mar 17, 2018

Important note: I get this error only on flutter master, not in the beta channel.

@xqwzts
Copy link
Contributor

xqwzts commented Mar 17, 2018

Thanks for the details.

You're seeing this on master and not beta because master currently has the --preview-dart-2 flag switched on by default.

Unfortunately this has introduced some breaking changes, in your case the issue is that invokeMethod, returns a Future<dynamic> and not a Future<bool> - which the FlutterBlue plugin is expecting.

The solution would be for the plugin to change the implementation of isOn [and other calls to invokeMethod] from the current:

Future<bool> get isOn => _channel.invokeMethod('isOn');

To:

Future<bool> get isOn async {
 final bool isOn = await _channel.invokeMethod('isOn');
 return isOn;
}

This is a third party plugin, I've opened an issue on FlutterBlue's repository to bring it to their attention.

@lovasoa
Copy link
Author

lovasoa commented Mar 17, 2018

But shouldn't this be a compile-time error rather than a runtime error, then ?

@xqwzts
Copy link
Contributor

xqwzts commented Mar 17, 2018

I believe you still need to pass the preview-dart-2 flag to the analyzer for it to pick these up:

flutter analyze --preview-dart-2.

@mraleph
Copy link
Member

mraleph commented Mar 18, 2018

@lovasoa It is not a compile time error because assigning an expression of type Future<dynamic> to a variable of type Future<bool> is an implicit down cast, e.g. this code will not trigger any compilation errors or any analyzer errors by default:

Future<dynamic> foo() {
  return new Future<dynamic>.value(10);
}

Future<String> bar() {
  return foo();  // will throw in runtime, no errors in compile time
  // this is the same as: return foo() as Future<String>;
}

You can make analyzer report errors in such places if you add implicit-casts: false to your analysis_options.yaml like described here.

For more information about Dart 2 breaking changes see this announcement on flutter-dev.

Hope this clarifies things a bit.

I do agree that an error message could be made better. I have filed dart-lang/sdk#32564 to address this.

@lovasoa
Copy link
Author

lovasoa commented Mar 18, 2018

@mraleph Thank you for the informative answer !

But I am still a little confused. In your example, there is indeed a type issue: the value inside the Future returned by foo is an int, whereas bar returns a Future<String>. In the case described here with flutterBlue, the dynamic value returned by _channel.invokeMethod("isOn") is actually a boolean, as expected.

So where does the error come from ? I think the code should either not compile, or compile and work as long as the dynamic value actually has the expected type. What is the point of compiling a code that can only fail ?

@mraleph
Copy link
Member

mraleph commented Mar 18, 2018

Let me amend my example a bit:

Future<dynamic> foo() {
  return new Future(() => something ? 11 : “abc”);
}

Future<dynamic> is a future that can produce a value of any type, while Future<String> can only produce a value of type String. So you can use Future<String> in contexts that expect Future<dynamic> because those contexts can deal with anything, but the other way around does not work - you can pass Future<dynamic> into contexts that expect Future<String> because those can’t deal with non-String values.

@lovasoa
Copy link
Author

lovasoa commented Mar 18, 2018

Yes, but in your example, I would expect the runtime error to be raised only when something is true (when the function returned a value of the wrong type). What is the point of doing the type check at runtime if it can only fail ?

@mraleph
Copy link
Member

mraleph commented Mar 18, 2018

@lovasoa type of new Future(() => something ? 11 : “abc”) is Future<dynamic> independent of the value of something.

Additionally you can take Future<String> and store it in the variable of type Future<dynamic> without any issues because Dart's generics are covariant.

Here is a more expanded example:

Future<dynamic> foo1({bool intValue}) {
  return new Future<dynamic>(() => intValue ? 10 : "abc"); 
}

Future<dynamic> foo2({bool intValue}) {
  return  intValue ? new Future<int>.value(10) : new Future<String>.value("abc");
}

void canHandleAnything(Future<dynamic> f) {
  f.then((dynamic value) => print(value));
}

Future<int> canHandleOnlyInts(Future<int> f) {
  return f.then((int x) => x * 10);
}

// Perfectly fine: passing Future<dynamic> to a function that expects Future<dynamic>
canHandleAnything(foo1(intValue: true)); 
canHandleAnything(foo1(intValue: false));

// throws: can't pass Future<dynamic> to a function that expects Future<int>
// even if Future<dynamic> in the end might end up producing int.
canHandleOnlyInts(foo1(intValue: true)); 
canHandleOnlyInts(foo1(intValue: false)); 

// Perfectly fine: passing Future<int> or Future<String> to a function that expects Future<dynamic>
canHandleAnything(foo2(intValue: true)); 
canHandleAnything(foo2(intValue: false)); 

// Perfectly fine: passing Future<int> to a function expecting Future<int>
// Note that here Future<int> is "hiding" inside the expression of type Future<dynamic>
canHandleOnlyInts(foo2(intValue: true)); 

// throws: can't pass Future<String> to a function expecting Future<int>
// Note that here Future<int> is "hiding" inside the expression of type Future<dynamic>
canHandleOnlyInts(foo2(intValue: false)); 

In other words if you have an expression of type Future<dynamic> it might evaluate to Future<dynamic>, but it can also evaluate to Future<String> or Future<T> for any other T.

Which means when you do assignment like this

Future<dynamic> v0 = ...;
Future<String> v1 = v0;

you can't know whether assignment will fail or not in compile time.

Now, how did things work before? In Dart 1 you could have taken Future<dynamic> and stored it in a variable of type Future<int>.

// Valid Dart 1 code that throws nothing in runtime. v contains a value of type Future<dynamic>.
Future<int> fu = foo1(intValue: false);

// This would throw only when fu completes and it tries to invoke closure that expects
// int value with a string argument.
fu.then((int x) => x * 10);

In Dart 1 if you have a variable of type Future<int> that did not guarantee that future would actually complete with int value, because the variable could contain Future<dynamic> If you had an expression of type List<String> that list might actually be List<dynamic> and contain integers. In Dart 2 - this can't happen.

@lovasoa
Copy link
Author

lovasoa commented Mar 18, 2018

Thank you very much for this explanation! The thing I did not understand was that even if Future<dynamic> is a supertype of Future<bool>, a value of type Future<dynamic> that has a bool in it is not a Future<bool>.

@branflake2267
Copy link

Leaving a note for searching... Using flutter master... This affects the firebase admob plugin.

FirebaseAdMob.instance.initialize(appId: appId);

screen shot 2018-03-19 at 7 33 24 pm

@prabhuthiran
Copy link

I also faced same type of issue, i just added async to the function. It works for me!!

@Hixie
Copy link
Contributor

Hixie commented Jun 12, 2018

The original issue here is that the flutterBlue library needs to be updated for Dart2. Since that's not a Flutter issue, I'm going to close this bug.

@Hixie Hixie closed this as completed Jun 12, 2018
@github-actions
Copy link

github-actions bot commented Sep 3, 2021

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 3, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants