PS: In many situations, 80% of known effects come from 20% of possible causes.

This article covers the basics of Flutter development, including Navigator components, Flex layout, image loading, Widget lifecycle, and hybrid development.

  • Detailed description of the use of the Flutter Navigator
  • Detailed description of the Flex layout of the Flutter series
  • Detailed explanation of image loading of the Flutter series
  • The Widget lifecycle of the Flutter family
  • The Android hybrid Development chapter of the Flutter series
  • Detailed description of the use of the Platform Channel of the Flutter series
  • Flutter hybrid development plays on the Android client

The following describes the use of Platform Channel in the development of Flutter. The main contents are as follows:

  1. Platform Channel Introduction
  2. Platform data type comparison
  3. BasicMessageChannel
  4. MethodChannel
  5. EventChannel

Platform Channel Introduction

Platform Channel is an asynchronous message Channel. Messages are encoded into binary messages before being sent. The received binary messages are decoded into Dart values. The communication architecture of Native and Flutter is shown as follows:

Flutter defines three different types of platformchannels, including the following:

  • BasicMessageChannel: Used for data transfer;
  • MethodChannel: Used to pass method calls;
  • EventChannel: Used to pass events;

The constructors of Flutter need to specify a channel identifier, decode the encoder, and BinaryMessenger. BinaryMessenger is a tool for Flutter to communicate with the platform, passing binary data, setting up the corresponding message handler, and so on.

There are two methods for uncoders: MethodCodec, which corresponds to methods, and MessageCodec, which corresponds to messages. BasicMessageChannel uses MessageCodec, MethodChannel and EventChannel use MethodCodec.

Platform data type comparison

Platform Channel provides different message decoding mechanisms. For example, StandardMessageCodec provides decoding of basic data types, JSONMessageCodec supports decoding of Json, etc., which are automatically converted when communicating between platforms. Comparison of data types on various platforms is as follows:

BasicMessageChannel

BasicMessageChannel is mainly used for data transfer, including binary data. With BasicMessageChannel, the functions of MethodChannel and EventChannel can be implemented. BasicMessageChannel to implement the Android project using Flutter resource files. The key flow is as follows:

  1. The binary data corresponding to the image resource can be obtained by the Flutter terminal. BinaryCodec is used here, so the data format is ByteData.
  2. BasicMessageChannel is used to send the corresponding data of the image.
  3. It is received using ByteBuffer on the Android side and converted into A ByteArray, which is then parsed into a Bitmap for display.

The key codes of Flutter end are as follows:

/ / create BasicMessageChannel
_basicMessageChannel = BasicMessageChannel<ByteData>("com.manu.image", BinaryCodec());

// Get the ByteData corresponding to the image in assets
rootBundle.load('images/miao.jpg').then((value) => {
  _sendStringMessage(value)
});

// Send image data
_sendStringMessage(ByteData byteData) async {
  await _basicMessageChannel.send(byteData);
}

Copy the code

The key codes of Android are as follows:

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    Log.i(tag, "configureFlutterEngine")
    // Set the message handler
    BasicMessageChannel<ByteBuffer>(
        flutterEngine.dartExecutor, "com.manu.image", BinaryCodec.INSTANCE
    ).setMessageHandler { message, reply ->
        Log.i(tag, "configureFlutterEngine > message:$message")
        // Data conversion: ByteBuffer->ByteArray
        val byteBuffer = message as ByteBuffer
        imageByteArray = ByteArray(byteBuffer.capacity())
        byteBuffer.get(imageByteArray)
    }

    // The method handler used to set up Flutter to jump to Android
    MethodChannel(flutterEngine.dartExecutor, channel).setMethodCallHandler { call, result ->
        Log.i(tag, "configureFlutterEngine > method:${call.method}")
        if ("startBasicMessageChannelActivity" == call.method) {
            // Carry image data
            BasicMessageChannelActivity.startBasicMessageChannelActivity(this,imageByteArray)
        }
    }
}

// Display images from Flutter Assets
val imageByteArray = intent.getByteArrayExtra("key_image")
val bitmap = BitmapFactory.decodeByteArray(imageByteArray,0,imageByteArray.size)
imageView.setImageBitmap(bitmap)
Copy the code

In addition, BasicMessageChannel and BinaryCodec support the transfer of large memory blocks.

MethodChannel

MethodChannel is mainly used for method transfer. Naturally, Native methods and Dart methods can be passed, that is, Android Native methods can be called in Flutter through MethodChannel, and Android Dart methods can be called. Calls to each other are made through the invokeMethod method of MethodChannel and must communicate using the same channel identifier as follows:

  1. Flutter calls the Android method

The following uses MethodChannel to jump from Flutter to the Android native interface MainActivity:

/ * * *@desc FlutterActivity
 * @author jzman
 */
val tag = AgentActivity::class.java.simpleName;

class AgentActivity : FlutterActivity() {
    val tag = AgentActivity::class.java.simpleName;
    private val channel = "com.manu.startMainActivity"
    private var platform: MethodChannel? = null;

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        Log.d(tag,"configureFlutterEngine")
        platform = MethodChannel(flutterEngine.dartExecutor, channel)
        // Set the method handlerplatform!! .setMethodCallHandler(StartMethodCallHandler(this@AgentActivity))}companion object{
        /** * Create NewEngineIntentBuilder */
        fun withNewEngine(a): MNewEngineIntentBuilder? {
            return MNewEngineIntentBuilder(AgentActivity::class.java)
        }
    }

    /** * custom NewEngineIntentBuilder */
    class MNewEngineIntentBuilder(activityClass: Class<out FlutterActivity?>?) :
        NewEngineIntentBuilder(activityClass!!)

    /** * implements MethodCallHandler */
    class StartMethodCallHandler(activity:Activity) : MethodChannel.MethodCallHandler{
        private val context:Activity = activity
        override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
            if ("startMainActivity" == call.method) {
                Log.i(tag,"arguments:"+call.arguments)
                startMainActivity(context)
                // Callback the result to Flutter
                result.success("success")}else {
                result.notImplemented()
            }
        }
    }
}
Copy the code

You can also use the methodChannel. Result object to callback the Result to the Flutter, with the following Flutter ends:

/// State
class _PageState extends State<PageWidget> {
  MethodChannel platform;

  @override
  void initState(a) {
    super.initState();
    platform = new MethodChannel('com.manu.startMainActivity');
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      margin: EdgeInsets.fromLTRB(8.8.8.0),
      child: RaisedButton(
        onPressed: () {
          _startMainActivity();
        },
        child: Text("Flutter to Android"),),); }/// Jump to the native Activity
  void _startMainActivity(a) {
    platform.invokeMethod('startMainActivity'.'flutter message').then((value) {
      // Receive the returned data
      print("value:$value"); }).catchError((e) { print(e.message); }); }}Copy the code
  1. Android calls the Dart method

Call the Dart method getName in Flutter via MethodChannel.

/ * * *@desc MainActivity
 * @author jzman
 */
class MainActivity : FlutterActivity() {
    private val tag = MainActivity::class.java.simpleName;
    private val channel = "com.manu.startMainActivity"
    private var methodChannel: MethodChannel? = null
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btnGetDart.setOnClickListener {
            getDartMethod()
        }
    }

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        Log.i(tag,"configureFlutterEngine")
        methodChannel = MethodChannel(flutterEngine.dartExecutor,channel)
    }

    private fun getDartMethod(a){ methodChannel? .invokeMethod("getName".null.object :MethodChannel.Result{
            override fun success(result: Any?). {
                Log.i(tag,"success: "+result.toString())
                Toast.makeText(this@MainActivity,result.toString(),Toast.LENGTH_LONG).show()
            }

            override fun error(errorCode: String,errorMessage: String? ,errorDetails:Any?). {
                Log.i(tag,"error")}override fun notImplemented(a) {
                Log.i(tag,"notImplemented")}}}companion object{
        fun startMainActivity(context: Context) {
            val intent = Intent(context, MainActivity::class.java)
            context.startActivity(intent)
        }
    }
}
Copy the code

The Flutter ends are as follows:

/// State
class _PageState extends State<PageWidget> {
  MethodChannel platform;

  @override
  void initState(a) {
    super.initState();
    platform = new MethodChannel('com.manu.startMainActivity');

    // Listen to Android call the Flutter method
    platform.setMethodCallHandler(platformCallHandler);
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
  /// FLutter Method
  Future<dynamic> platformCallHandler(MethodCall call) async{
    switch(call.method){
      case "getName":
        return "name from flutter";
        break; }}}Copy the code

EventChannel

EventChannel is mainly used for one-way calls between the Flutter and the native. It is used in a similar way to broadcast in Android. The native interface is responsible for sending events to the Flutter.

/// Android
class MFlutterFragment : FlutterFragment() {
    // Use Fragment here, same with Activity
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        Log.d(tag,"configureFlutterEngine")
        EventChannel(flutterEngine.dartExecutor,"com.manu.event").setStreamHandler(object:
            EventChannel.StreamHandler{
            override fun onListen(arguments: Any? , events:EventChannel.EventSink?). {
                Log.i(tag,"configureFlutterEngine > onListen")
                // EventSink sends an event notificationevents? .success("event message")}override fun onCancel(arguments: Any?). {
                Log.i(tag,"configureFlutterEngine > onCancel")}}}companion object{
        fun withNewEngine(a): NewEngineFragmentBuilder? {
            return MNewEngineIntentBuilder(
                MFlutterFragment::class.java
            )
        }
    }

    class MNewEngineIntentBuilder(activityClass: Class<out FlutterFragment?>?) :
        NewEngineFragmentBuilder(activityClass!!)
}
Copy the code

The Flutter ends are as follows:

/// State
class EventState extends State<EventChannelPage> {
  EventChannel _eventChannel;
  String _stringMessage;
  StreamSubscription _streamSubscription;

  @override
  void initState(a) {
    super.initState();
    _eventChannel = EventChannel("com.manu.event");
    // Listen for Event events
    _streamSubscription =
        _eventChannel.receiveBroadcastStream().listen((event) {
      setState(() {
        _stringMessage = event;
      });
    }, onError: (error) {
      print("event error$error");
    });
  }

  @override
  void dispose(a) {
    super.dispose();
    if(_streamSubscription ! =null) {
      _streamSubscription.cancel();
      _streamSubscription = null; }}@override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("EventChannel"),
          centerTitle: true,
        ),
        body: Center(
          child: Text(_stringMessage == null ? "default": _stringMessage), )); }}Copy the code

This is how the Flutter platform channels are used. You can obtain the source code of the Flutter platform by using the “Channel” keyword of the public account.