iosflutterflutter-pluginflutter-platform-channel

FlutterError Optional("type \'_Map<Object?, Object?>\' is not a subtype of type \'Future<dynamic>\'") when using invokeMethod:argument:result from iOS


I'm creating a Flutter plugin with some native iOS ViewController from a native iOS app. In the native ViewController I setup a MethodChannel and a MethodCallHandler, same in the Flutter screen. I'm using the StandartMessageCodec in both UIKitView and FlutterPlatformViewFactory which should support most iOS object types:

/// On iOS, messages are represented as follows: /// /// * null: nil /// * [bool]: NSNumber numberWithBool: /// * [int]: NSNumber numberWithInt: for values that are representable using /// 32-bit two's complement; NSNumber numberWithLong: otherwise /// * [double]: NSNumber numberWithDouble: /// * [String]: NSString /// * [Uint8List], [Int32List], [Int64List], [Float64List]: /// FlutterStandardTypedData /// * [List]: NSArray /// * [Map]: NSDictionary

When i use the invokeMethod:argument:result method from iOS dough, I do get its arguments across to Flutter, but the result callback in iOS is a FlutterError :

        pluginChannel.invokeMethod("retrieve_data", arguments: ["selectedVehicleType": selectedVehicleType] as! Any) { (r: Any) in
            print("@@ result from retrieve_data method call is: \(r)")
            if let error = r as? FlutterError {
                print("error is : \n\(error.code), \n\(error.message)")
            }
        }

error is : error, Optional("type '_Map<Object?, Object?>' is not a subtype of type 'Future'")

MethodChannel handler in Flutter:

    channel.setMethodCallHandler((call) {
      switch (call.method) {
        // test call for getting data from API
        // this is invoked with "result" parameter on native side which returns an error:
        // "type \'_Map<Object?, Object?>\' is not a subtype of type \'Future<dynamic>\'"

        case 'retrieve_data':
          print(
              '## retrieve_data received in NativeScreenExample with args \n ${call.arguments}, \n of type ${call.arguments.runtimeType}');
          // test invocation to send data from API to native
          dynamic result =
              channel.invokeMethod('data_retrieved', 'arg').then((result) {
            print(
                '## data_retireved method invoked with result data from native: $result');
            return result;
          });
          break;
        default:
      }
      return call.arguments;
    });

Can you spot something wrong with the setup?


Solution

  • I think, that handler (it is the callback you set in setMethodCallHandler) should return Future to MethodChannel correct work.

    To achieve this, you should add async keyword like this:

    channel.setMethodCallHandler((call) async {
       // your actual code here
    }
    

    After that change I could achieve Success: ["selectedVehicleType": Car] (I used "Car" as "selectedVehicleType") in my log in Xcode.

    Hope this helps!