56

I am writing a native plugin that, in some cases, has to call functions in the Flutter portion of the app, written in Dart. How it's achieved, is explained here: https://flutter.io/platform-channels/

Furthermore, an example of invoking a method from the native/platform part towards the Dart/non-native is here: https://github.com/flutter/plugins/tree/master/packages/quick_actions

Now, this example is really nice in case the platform only needs to invoke a method, i.e. that call returns nothing/void, but in case it needs to invoke a function, i.e. needs a return value from the non-native/Dart part, I could not have found an example or documentation on the internet. I believe it can be implemented though, because in the native Java part, there is a method:

public void invokeMethod(String method, Object arguments, MethodChannel.Result callback)

So, there is a callback object that could have a return value from the non-native part - or, I am mistaken here, and there is currently no way of returning a value from the non-native Dart portion of the app?

2beens
  • 743
  • 1
  • 6
  • 8
  • Can you look at this - https://stackoverflow.com/questions/63581385/flutter-plugin-call-back-on-dart ? –  Aug 26 '20 at 18:19

1 Answers1

62

The signature is void setMethodCallHandler(Future<dynamic> handler(MethodCall call)), so we need to provide a function at the Dart end that returns Future<dynamic>, for example _channel.setMethodCallHandler(myUtilsHandler);

Then implement the handler. This one handles two methods foo and bar returning respectively String and double.

  Future<dynamic> myUtilsHandler(MethodCall methodCall) async {
    switch (methodCall.method) {
      case 'foo':
        return 'some string';
      case 'bar':
        return 123.0;
      default:
        throw MissingPluginException('notImplemented');
    }
  }

At the Java end the return value is passed to the success method of the Result callback.

channel.invokeMethod("foo", arguments, new Result() {
  @Override
  public void success(Object o) {
    // this will be called with o = "some string"
  }

  @Override
  public void error(String s, String s1, Object o) {}

  @Override
  public void notImplemented() {}
});

In Swift, the return value is an Any? passed to the result closure. (Not implemented is signaled by the any parameter being the const NSObject value FlutterMethodNotImplemented.)

channel.invokeMethod("foo", arguments: args, result: {(r:Any?) -> () in
  // this will be called with r = "some string" (or FlutterMethodNotImplemented)
})
Richard Heap
  • 48,344
  • 9
  • 130
  • 112
  • 1
    Thanks for it! Do you have any sample code to call from IOS, Swift? Thanks a lot! – Tom Jul 29 '19 at 12:32
  • 2
    @Tom - added Swift example – Richard Heap Jul 29 '19 at 18:02
  • @RichardHeap would this when the add isn;t active? For example when the android alarmmanager triggers a broadcast receiver? – Robin Dijkhof Aug 23 '19 at 17:01
  • How to use this in a Flutter plugin? the Android code will return the message to plugin.dart file method but how does the main.dart file use this method? – Darshan Pania Nov 03 '19 at 17:49
  • Your plugin needs an `initialize` or similar method. You call this from `main` or similar, passing some functions or a concrete implementation of an interface. The plugin remembers these/this. Then when you get a call from native it flows like this: main (the function/interface mentioned above) <- plugin/Dart <- method channel <- plugin/native <- native call – Richard Heap Nov 03 '19 at 21:31
  • See also this answer where the question was "is it possible to not have the 'initialize' function" to which the answer is, of course, no. https://stackoverflow.com/questions/58573536/is-there-in-flutter-a-way-of-calling-an-init-function-in-the-plugin-automatica/58576266#58576266 – Richard Heap Nov 03 '19 at 21:32
  • I have this error: `C:\...\ApplinkActivity.java:56: error: cannot find symbol CHANNEL.invokeMethod("foo", 12, new Result() { symbol: class Result location: class ApplinkActivity` What is `Result`? – Mohsen Emami Dec 04 '19 at 05:46
  • `Result` is an interface that you have to implement. Best to ask your own question, showing your code. – Richard Heap Dec 04 '19 at 11:29
  • how do you instantiate `channel` on the java/kotlin side?? – codeKiller Jan 20 '20 at 12:29
  • @codeKiller You construct it with `new MethodChannel` in your `onAttachedToEngine` implementation. For a good example, see: https://github.com/flutter/plugins/tree/master/packages/connectivity/android/src/main/java/io/flutter/plugins/connectivity – Richard Heap Jan 20 '20 at 14:07
  • @RichardHeap When I am passing a String list as argument (i.e. channel.invokeMethod("foo", stringList, new Result()) it is giving me this error type 'List' is not a subtype of type 'String'. Any idea how it can be fixed?? – Ayush Malviya Feb 11 '20 at 06:55
  • @Ayush I'd need more context. Maybe ask a new question. – Richard Heap Feb 11 '20 at 11:39
  • @RichardHeap what is the specific code on the dart-side (your default case with a // todo) that will trigger a FlutterMethodNotImplemented / notImplemented on the caller's end? I couldn't find a reference. – entonio Jul 06 '20 at 03:39
  • @entonio I think `throw MissingPluginException('notImplemented');` should do it – Richard Heap Jul 06 '20 at 13:18
  • @RichardHeap indeed, thank you. I took the liberty to edit your answer with it because I think the documentation is lacking. – entonio Jul 08 '20 at 01:29
  • @RichardHeap Can you guide on this - https://stackoverflow.com/questions/63581385/flutter-plugin-call-back-on-dart –  Aug 26 '20 at 18:18
  • Did not understand how to pass arguments from *native side*. Please give a sample. I really need to pass a list of objects – David Papirov Jul 07 '21 at 08:23
  • 1
    @DavidPapirov To pass a list use the platform equivalent - e.g. for Java use `ArrayList`. Pass it as the `args` parameter - see https://stackoverflow.com/questions/57035150/how-to-pass-array-of-strings-with-invokemethod-from-native-to-flutter Note that you cannot pass an arbitrary object unless it's on the list of types supported by the message codec (e.g. string, map, etc). You could turn arbitrary objects into maps of supported items, or JSON, or write a custom codec. – Richard Heap Jul 07 '21 at 15:24
  • 1
    @DavidPapirov See also: https://stackoverflow.com/questions/61507418/how-to-put-arguments-from-swift-native-code-to-flutter/61509073#61509073 – Richard Heap Jul 07 '21 at 15:25