There are three ways to communicate between Flutter and Native terminal:

MethodChannel: The Flutter and the Native can call each other, and the result can be returned after the call. The Native can call the Flutter actively, and the Flutter can call the result actively, which is two-way communication. This is the most common method, and Native calls need to be executed in the main thread.

BasicMessageChannel: Used to encode and decode messages using a specified codec. It is bidirectional communication and can be actively invoked by either the Native end or the Flutter.

EventChannel: Used for the communication of Event Streams. The Native end actively sends data to Flutter, which is usually used to monitor the state of Flutter, such as network changes and sensor data.

The Flutter communicates asynchronously with the Native end.

When the Native endpoint of the Flutter actively sends data to the main thread, the Native endpoint code needs to be executed in the main thread. The Android endpoint jumps from the child thread to the main thread. Handler(looper.getMainLooper ()).post {} Java code:

new Handler(Looper.getMainLooper()).post(new Runnable() {
  @Override
  public void run() {

  }
});
Copy the code

If the current Activity is available, you can also use the following method: activity.runonuithread {}

Dispatch_async (dispatch_get_main_queue(), ^{}); Swift code: dispatchqueue.main.async {}

The Flutter end creates a MethodChannel to communicate with the native end:

var channel = MethodChannel('com.flutter.guide.MethodChannel'); Com. Flutter. Guide. MethodChannel is MethodChannel, the name of the primary side and the matching.Copy the code

Send a message:

 var result = await channel.invokeMethod('sendData',{'name': 'laomeng', 'age': 18})
Copy the code

The first parameter represents method, the name of the method, which is parsed by the native end. The second parameter represents a parameter of any type. Map is usually used for multiple parameters. Return the Future, the data returned by the native end. Complete code:

class MethodChannelDemo extends StatefulWidget { @override _MethodChannelDemoState createState() => _MethodChannelDemoState(); } class _MethodChannelDemoState extends State<MethodChannelDemo> { var channel = MethodChannel('com.flutter.guide.MethodChannel'); var _data; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Column( children: [SizedBox(height: 50,), RaisedButton(Child: Text(' send data to native '), onPressed: () async { var result = await channel .invokeMethod('sendData', {'name': 'laomeng', 'age': 18}); var name = the result (' name '), var age = result [' age ']; setState (() {_data while forming = '$name, $age;});},), Text (" native return data: $_data') ], ), ); }}Copy the code

Create MethodChannelDemo on Android:

 package com.flutter.guide

import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel

/**
 * des:
 */
class MethodChannelDemo(messenger: BinaryMessenger): MethodChannel.MethodCallHandler {

    private var channel: MethodChannel

    init {
        channel = MethodChannel(messenger, "com.flutter.guide.MethodChannel")
        channel.setMethodCallHandler(this)
    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {

    }
}
Copy the code

The onMethodCall method calls the invokeMethod callback on the Flutter side as follows:

 override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
    if (call.method == "sendData") {
        val name = call.argument("name") as String?
        val age = call.argument("age") as Int?

        var map = mapOf("name" to "hello,$name",
                "age" to "$age"
        )
        result.success(map)
    }
} 
Copy the code

The call.method string is the method passed in by the invokeMethod method. Call. argument is the argument passed to the invokeMethod. Since the Flutter end is passed to a Map, the above resolution is resolved according to the Map. Result.success () is the result returned to Flutter. Var result = await channel. invokeMethod(‘sendData’, {‘name’: ‘laomeng’, ‘age’: 18}); var name = result[‘name’]; var age = result[‘age’]; The parsing at both ends should correspond to each other. Start in MainActivity:

 class MainActivity : FlutterActivity() {
 override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannelDemo(flutterEngine.dartExecutor.binaryMessenger)
    }
}
Copy the code

IOS To create MethodChannelDemo on iOS, do as follows:

import Flutter
import UIKit

public class MethodChannelDemo {

    init(messenger: FlutterBinaryMessenger) {
        let channel = FlutterMethodChannel(name: "com.flutter.guide.MethodChannel", binaryMessenger: messenger)
        channel.setMethodCallHandler { (call:FlutterMethodCall, result:@escaping FlutterResult) in
            if (call.method == "sendData") {
                if let dict = call.arguments as? Dictionary<String, Any> {
                    let name:String = dict["name"] as? String ?? ""
                    let age:Int = dict["age"] as? Int ?? -1
                    result(["name":"hello,\(name)","age":age])
                }
            }
        }
    }
}  
Copy the code

Start in AppDelegate:

import UIKit import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let controller : FlutterViewController = window? .rootViewController as! FlutterViewController MethodChannelDemo(messenger: controller.binaryMessenger) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }Copy the code

The native end actively sends messages to the Flutter end to receive data

@override
void initState() {
  super.initState();
  channel.setMethodCallHandler((call) {
    setState(() {
      _nativeData = call.arguments['count'];
    });
  });
} 
Copy the code

The Android native starts a timer to send data to the Flutter every second.

class MethodChannelDemo(var activity: Activity, messenger: BinaryMessenger) : MethodChannel.MethodCallHandler {

    private var channel: MethodChannel
    private var count = 0

    init {
        channel = MethodChannel(messenger, "com.flutter.guide.MethodChannel")
        channel.setMethodCallHandler(this)
        startTimer()
    }


    fun startTimer() {
        var timer = Timer().schedule(timerTask {
            activity.runOnUiThread {
                var map = mapOf("count" to count++)
                channel.invokeMethod("timer", map)
            }
        }, 0, 1000)

    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        if (call.method == "sendData") {
            val name = call.argument("name") as String?
            val age = call.argument("age") as Int?

            var map = mapOf("name" to "hello,$name",
                    "age" to "$age"
            )
            result.success(map)
        }
    }
} 
Copy the code

Note: The data sent by Android should be called in the main scene, i.e. :

Activity.runonuithread {var map = mapOf("count" to count++) channel.invokemethod ("timer", map)}  class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannelDemo(this,flutterEngine.dartExecutor.binaryMessenger) flutterEngine.plugins.add(MyPlugin()) } }Copy the code

IOS Sends data. The code for the iOS timer is as follows:

 import Flutter
import UIKit

public class MethodChannelDemo {
    var count =  0
    var channel:FlutterMethodChannel
    init(messenger: FlutterBinaryMessenger) {
        channel = FlutterMethodChannel(name: "com.flutter.guide.MethodChannel", binaryMessenger: messenger)
        channel.setMethodCallHandler { (call:FlutterMethodCall, result:@escaping FlutterResult) in
            if (call.method == "sendData") {
                if let dict = call.arguments as? Dictionary<String, Any> {
                    let name:String = dict["name"] as? String ?? ""
                    let age:Int = dict["age"] as? Int ?? -1
                    result(["name":"hello,\(name)","age":age])
                }
            }
        }
        startTimer()
    }

    func startTimer() {
        var timer = Timer.scheduledTimer(timeInterval:1, target: self, selector:#selector(self.tickDown),userInfo:nil,repeats: true)
    }
    @objc func tickDown(){
        count += 1
        var args = ["count":count]
        channel.invokeMethod("timer", arguments:args)
    }
}
Copy the code