Everything that Flutter communicates with native

Introduction to Flutter

Introduction to the

Communication between Flutter and native relies on flexible messaging:

  • The Flutter part of an application sends messages to the host (iOS or Android) application (native app) of its application via a platform channel.
  • The host listens on the platform channel and receives the message. It then invokes the platform’s API and sends the response back to the client, the Flutter part of the application.
  1. MethodChannel // The Flutter calls the native method for method abandonment
  2. BasicMessageChannel // The Flutter sends messages to each other with the native for data transfer
  3. EventChannel natively sends messages that are received by Flutter for data flow messages

Data structures that can be passed

Dart Android iOS
null null nil(NSNull when nested)
bool Java.lang.Boolean NSNumber numberWithBool:
int Java.lang.Integer NSNumber numberWithInt:
int, if 32 bits not enough Java.lang.Long NSNumber numberWithLong:
int, if 64 bits not enough Java.lang.BigInteger FlutterStandardBigInteger
double Java.lang.Double NSNumber numberWithDouble
String java.lang.String NSString
Unit8List byte[] FlutterStandardTypedData typedDataWithBytes:
Int32List int[] FlutterStandardTypedData typedDataWithInt32:
Int64List long[] FlutterStandardTypedData typedDataWithInt64:
Float64List double[] FlutterStandardTypedData typedDataWithFloat64:
List Java.util.ArrayList NSArray
Map Java.util.HashMap NSDictionary

MethodChannel (calling methods to each other)

Android calls the Flutter method:

Android:

  1. Initialize MethodChannel
FlutterView (getFlutter in MainActivity); 2. Name constant, used in Flutter with the same name
MethodChannel methodChannel = newMethodChannel (flutterView, "testflutter");Copy the code
  1. Call the Flutter method
private void invokeFlutterMethod(a) {
  if (this.mMethodChannel ! =null) {
    this.mMethodChannel.invokeMethod("flutterMethod"."Native parameters".new MethodChannel.Result() {
      @Override
      public void success(Object o) {
        Toast.makeText(mContext, o.toString(), Toast.LENGTH_LONG).show();
      }
      @Override
      public void error(String s, String s1, Object o) {}@Override
      public void notImplemented(a) {}}); }}Copy the code

Call invokeMethod with MethodChannel (” method name “,” pass parameters “,[Flutter returns parameter callback, optional]);

Flutter:

  1. Initialize MethodChannel
static const methodChannel = const MethodChannel('testflutter');
Copy the code
  1. Add processing methods to MethodChannel
methodChannel.setMethodCallHandler(_addNativeMethod);
Copy the code
  1. Handles the method called by Android, according to the method name
Future<dynamic> _addNativeMethod(MethodCall methodCall) async {
  switch (methodCall.method) {
    case 'flutterMethod':
      setState(() {
        _calledFromNative = 'flutter method called from native with param ' + methodCall.arguments;
      });
      return 'flutter method called from native with param ' + methodCall.arguments;
      break; }}// Where, the data returned by return is received in the Android callback
Copy the code

Flutter calls the Android method:

Android:

  1. Initialize the MethodChannel and add a custom plugin
MethodChannel methodChannel = new MethodChannel(flutterView, METHOD_CHANNEL);
methodChannel.setMethodCallHandler(plugin);
Copy the code
  1. A custom plugin implementation MethodChannel. MethodCallHandler onMethodCall method of interface
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
  // the method of Flutter calls Native
  if (methodCall.method.equals("getBatteryLevel")) {
    int batteryLevel = getBatteryLevel();
    if(batteryLevel ! = -1) {
      result.success(batteryLevel);
    } else {
      result.error("UNAVALIABLE"."battery level unavaliable".null); }}else{ result.notImplemented(); }}// Listen in onMethodCall for a method called by the name of the Flutter (here getBatterLevel), and return the result of the method execution via result.
Copy the code

Flutter:

  1. Initialize MethodChannel
static const methodChannel = const MethodChannel('testflutter');
Copy the code
  1. Call the Android method and receive the returned data
// The methods of the method channel are asynchronous
Future<Null> _getBatteryLevel() async {
  String batteryLevel;
  try {
    final int result = await methodChannel.invokeMethod('getBatteryLevel');
    batteryLevel = 'Battery level $result .';
  } on PlatformException catch (e) {
    batteryLevel = 'Battery level unknown ${e.message}';
  }  
  setState(() {
    _batteryLevel = batteryLevel;
  });
}
Copy the code

BasicMessageChannel

Android messages to Flutter:

Android:

  1. Initialize BasicMethodChannel
BasicMessageChannel messageChannel = new BasicMessageChannel<>(flutterView, "messageChannel", StandardMessageCodec.INSTANCE);
Copy the code
  1. Call the method that sends the message
private void sendMessageToFlutter(a) {
  if (this.mBasicMessageChannel ! =null) {
    this.mBasicMessageChannel.send("Message From Native"); }}Copy the code

Flutter:

  1. Initialize BasicMessageChannel
static const basicMessageChannel = BasicMessageChannel('messageChannel', StandardMessageCodec());
Copy the code
  1. Add received message processing methods
void _listenMessageFromNative() {
  basicMessageChannel.setMessageHandler(_receiveMessageFromNative);
}
Copy the code
  1. Process the received data
//Flutter receives messages from Native
Future<dynamic> _receiveMessageFromNative(Object result) async {
  setState(() {
    _messageFromNative = result.toString();
  });
}
Copy the code

Flutter sends a message to Android:

Android:

  1. Initialize BasicMessageChannel and add plugin to handler
BasicMessageChannel messageChannel = new BasicMessageChannel<>(flutterView, "messageChannel", StandardMessageCodec.INSTANCE);
messageChannel.setMessageHandler(plugin);
Copy the code
  1. The plugin implementation BasicMessageChannel MessageHandler interface onMessage method, processing the received information
@Override
public void onMessage(Object o, BasicMessageChannel.Reply reply) {
  Toast.makeText(mContext, o.toString(), Toast.LENGTH_LONG).show();
  reply.reply(o.toString()+" back from native");
}
// Reply returns data to the Flutter
Copy the code

Flutter:

  1. Initialize BasicMessageChannel
static const basicMessageChannel = BasicMessageChannel('messageChannel', StandardMessageCodec());
Copy the code
  1. Send a message to Android and receive the returned data
Future<dynamic> _sendMessageToNative(String message) async {
  String reply = await basicMessageChannel.send(message);
  print(reply);
  setState(() {
    _replayFromNative = reply;
  });
  return reply;
}
Copy the code

EventChannel (native message sending, Flutter receiving)

Android:

  1. Initialize EventChannel and add plugin to handler
EventChannel eventChannel = new EventChannel(flutterView, EVENT_CHANNEL);
eventChannel.setStreamHandler(plugin);
Copy the code
  1. The plugin implementation EventChannel. StreamHandler interface and onListen and onCancel method
  2. Send messages to Flutter in onListen via an instance of eventChannel. EventShink
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
  BroadcastReceiver chargingBroadcastReceiver = createChargingBroadcaseReceiver(eventSink);
  mContext.registerReceiver(chargingBroadcastReceiver,new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
@Override
public void onCancel(Object o) {}private BroadcastReceiver createChargingBroadcaseReceiver(EventChannel.EventSink eventSink) {
  return new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
      if (status == BatteryManager.BATTERY_STATUS_UNKNOWN) {
        eventSink.error("UNAVALIABLE"."charging status is unavailable".null);
      } else {
        boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING;
        eventSink.success(isCharging ? "charging" : "disCharging"); }}}; }Copy the code

Flutter:

  1. Initialize the EventChannel
static const _eventChannel = const EventChannel('charging');
Copy the code
  1. Add a method to receive data
void listenNativeEvent() {
  _eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
}
// Receive the returned data
void _onEvent(Object object) {
  String s = "Battery is ${object == 'charging' ? ' ' : 'dis'}Charging";
  setState(() {
    _batteryStatus = s;
  });
}
Copy the code

Bridge View for use by Flutter

Android:

  1. Custom View, inherited from PlatformView
public class MyTextview implements PlatformView {
  TextView t;
  public MyTextview(Context context, MessageCodec<Object> messenger, int id, Map<String, Object> params){
    TextView textView = new TextView(context);
    // Get parameters, if any parameters are passed
    if (params.containsKey("text")){
      textView.setText(params.get("text").toString());
    }
    this.t = textView;
  }
  @Override
  public View getView(a) {
    return t;
  }
  
  @Override
  public void dispose(a) {}}Copy the code
  1. Implement PlatformViewFactory
public class TextViewFactory extends PlatformViewFactory {
  private MessageCodec<Object> messageCodec;
  public TextViewFactory(MessageCodec<Object> createArgsCodec) {
    super(createArgsCodec);
    this.messageCodec = createArgsCodec;
  }
  
  @Override
  public PlatformView create(Context context, int i, Object o) {
    return newMyTextview(context, messageCodec, i, (Map<String, Object>) o); }}Copy the code
  1. Register a View for Flutter
registrar.platformViewRegistry().registerViewFactory("TextView".new TextViewFactory(new StandardMessageCodec()));
// Call the Flutter TextView and use it as a viewType
Copy the code

Flutter:

  1. Use a bridged View
AndroidView(
  viewType: 'TextView',
  creationParams: {'text': 'TTTeeeXXXttt'},
  creationParamsCodec: new StandardMessageCodec(),
),// creationParams and creationParamsCodec must exist together or not
Copy the code

Above, the end of the text;

Officials have other suggestions

Publish pub using:

  1. Hosted Packages (posted to pub.dartlang.org)

    $flutter packages pub publish --dry-run
    $flutter packages pub publish
    Copy the code

    Use in the YAML file like other dependencies.

  2. Git packages

    Upload the code to Git and tag it

    Yaml file references

    dependencies:
    	flutter_remote_package:
    		git:
    			url: git@gitlab. ref:0.01. // Can be commit, branch, or tag
    Copy the code
  3. local

    Create the plugins folder under the root of the Flutter App and move the plugin to the plugins folder.

    Dependencies: Flutter_plugin_batterylevel: path: plugins/flutter_plugin_batterylevelCopy the code

    This is limited to creating projects using plugins. Sometimes they are developed in their own Android or iOS projects, so it is not so convenient to release them separately.

Sometimes you need to run channelMethod to the UI Thread. On Android, you need to post a Runnable to the Android UI thread.

new Handler(Looper.getMainLooper()).post(new Runnable() {
  @Override
  public void run(a){
    // call the desired channel message here.}})Copy the code
  1. The essence of “passing View” is to pass texture ID. We only need to understand that Flutter realizes external texture through Presentation. When creating the Presentation, we pass in the corresponding Context of FlutterView and create a virtual display object. Enable Flutter to find texture data created by Native using ID directly.

  2. Event processing: At the stage of transferring from Native to Flutter, the Flutter processes the event according to its own rules. If the Event is captured by AndroidView, it will be encapsulated into the corresponding event at the Native end and sent back to The Native end through the method channel, where the event will be processed by the Native end.

    For possible sliding-time conflicts, please refer to the official notes:

    /// For example, with the following setup vertical drags will not be dispatched to the Android view as the vertical drag gesture is claimed by the parent [GestureDetector].
    /// 
    /// GestureDetector(
    /// onVerticalDragStart: (DragStartDetails d) {},
    /// child: AndroidView(
    /// viewType: 'webview',
    ///     gestureRecognizers: <OneSequenceGestureRecognizer>[].
    / / /),
    / / /)
    /// 
    /// To get the [AndroidView] to claim the vertical drag gestures we can pass a vertical drag gesture recognizer in [gestureRecognizers] e.g:
    /// 
    /// GestureDetector(
    /// onVerticalDragStart: (DragStartDetails d) {},
    /// child: SizedBox(
    / / / width: 200.0.
    / / / height: 100.0.
    /// child: AndroidView(
    /// viewType: 'webview',
    ///       gestureRecognizers: <OneSequenceGestureRecognizer>[new VerticalDragGestureRecognizer()],
    / / /),
    / / /),
    / / /)
    Copy the code

    [making] github.com/damengzai/f…

Reference:

The correct position to embed Native components in a Flutter is… — Idle fish technique