preface
Practical development usually requires that the Flutter invoke the Native function, or the Native invoke the Flutter function
The communication between them is mainly achieved through Platform Channel, and there are three main channels:
- MethodChannel is used to pass method calls
- EventChannel is used for the communication of Event Streams
- BasicMessageChannel is used to pass strings and semi-structured information
The following figure shows message passing between Flutter and Native using MethodChannel as an example:
To ensure the smoothness of the application and timely response to user operations, message and response transfer between Flutter and Native is asynchronous, but the channel API needs to be called in the main thread
Data types supported by Platform Channel
Platform Channel uses standard message codecs to automatically serialize and deserialize data for us as we send and receive it
The codec supports the following data types:
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: |
double | java.lang.Double | NSNumber numberWithDouble: |
String | java.lang.String | NSString |
Uint8List | 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
Take Flutter to obtain mobile battery power as an example. In order to obtain Android/iOS battery power in Flutter interface, the function of obtaining battery power should be written in Native to be called by Flutter
Native client code
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example.flutter_battery/battery"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); New MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler((call, result) -> {// executes in the main threadif (call.method.equals("getBatteryLevel"Int batteryLevel = fetchBatteryLevel();if(batteryLevel ! Result. success(batteryLevel); }else {
result.error("UNAVAILABLE"."Battery level not available.", null); }}else{ result.notImplemented(); }}); } // Get the power private intfetchBatteryLevel() {
int batteryLevel;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(getApplicationContext()).
registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
returnbatteryLevel; }}Copy the code
In Native code, we create a new fetchBatteryLevel function to get the battery, and then a new MethodChannel object
Note here that the constructor’s second argument, CHANNEL, is also a string that will be used later in Flutter
Finally, we set up the MethodChannel function to call the handler MethodCallHandler. This MethodCallHandler is called when the Flutter calls the Native function
Flutter server-side code
Class _MyHomePageState extends State<MyHomePage> {// The constructor argument is the Android CHANNEL constant static const methodChannelBattery = const MethodChannel('com.example.flutter_battery/battery');
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
// invokeMethod('getBatteryLevel') will the callback MethodCallHandler final int result = await methodChannelBattery. InvokeMethod ('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
} on MissingPluginException catch (e) {
batteryLevel = "plugin undefined";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
margin: EdgeInsets.only(left: 10, top: 10),
child: Center(
child: Column(
children: [
Row(
children: <Widget>[
RaisedButton(
child: Text(
'GetBatteryFromNative', style: TextStyle(fontSize: 12), ), onPressed: _getBatteryLevel, ), Padding( padding: EdgeInsets.only(left: 10), child: Text(_batteryLevel), ) ], ), ], ), ), ), ); }}Copy the code
Click the button on the Flutter screen to get the battery on your Android phone:
MethodChannel can also call Native Flutter functions in addition to implementing a Flutter
Call the invokeMethod method on the Native side to specify which Flutter method you want to invoke:
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = fetchBatteryLevel();
if(batteryLevel ! = -1) { result.success(batteryLevel); }else {
result.error("UNAVAILABLE"."Battery level not available.", null); }}else{ result.notImplemented(); } // Native calls Flutter getFlutterContent channel.invokemethod ("getFlutterContent", null, new MethodChannel.Result() {
@Override
public void success(Object o) {
Log.e("BatteryPlugin"."Dart getFlutterContent() result : " + o);
}
@Override
public void error(String s, String s1, Object o) {
Log.e("BatteryPlugin"."Dart getFlutterContent() error : " + s);
}
@Override
public void notImplemented() {
Log.e("BatteryPlugin"."Dart getFlutterContent() notImplemented"); }}); }Copy the code
A MethodCallHandler for a MethodChannel is set up on a Flutter. The Native method calls the invokeMethod method.
void initState() {
super.initState();
methodChannelBattery.setMethodCallHandler(batteryCallHandler);
}
Future<dynamic> batteryCallHandler(MethodCall call) async {
switch (call.method) {
case "getFlutterContent":
return "This is FlutterContent"; }}Copy the code
The main meaning of the above code is that when we click the button to call the function in Native to get the power, then immediately call the getFlutterContent function in the Flutter in Native
The console will then output the return value from Flutter getFlutterContent() :
Dart getFlutterContent() result : This is FlutterContent
Copy the code
EventChannel
EventChannel is suitable for event stream communication. For example, Native needs to send frequent messages to Flutter, such as monitoring network status, Bluetooth devices, and so on, and then send them to Flutter
Here is an example of the use of EventChannel that sends one event per second to a Flutter in Native:
Native client code
public class EventChannelPlugin implements EventChannel.StreamHandler {
private Handler handler;
private static final String CHANNEL = "com.example.flutter_battery/stream"; private int count = 0; Public static void registerWith(pluginregistry.registrar) { The CHANNEL constant works the same as MethodChannel final EventChannel CHANNEL = new EventChannel(Registr.messenger (), CHANNEL); // Set the stream's handler (StreamHandler) channel.setStreamHandler(new EventChannelPlugin()); } @Override public void onListen(Object o, Eventchannel.eventsink EventSink) {// Every second number +1 handler = new handler (message -> {// Then send the number to Flutter eventSink.success(++count); handler.sendEmptyMessageDelayed(0, 1000);return false; }); handler.sendEmptyMessage(0); } @Override public void onCancel(Object o) { handler.removeMessages(0); handler = null; count = 0; }}Copy the code
Flutter server-side code
Class _MyHomePageState extends State<MyHomePage> {static const stream = const EventChannel('com.example.flutter_battery/stream');
int _count = 0;
StreamSubscription _timerSubscription;
void _startTimer() {
if(_timerSubscription == null) Will trigger the Native onListen callback _timerSubscription = stream. ReceiveBroadcastStream () listen (_updateTimer); } void_stopTimer() { _timerSubscription? .cancel(); _timerSubscription = null;setState(() => _count = 0);
}
void _updateTimer(dynamic count) {
print("-- -- -- -- -- -- -- --$count");
setState(() => _count = count);
}
@override
void dispose() { super.dispose(); _timerSubscription? .cancel(); _timerSubscription = null; } @override Widget build(BuildContext context) {return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
margin: EdgeInsets.only(left: 10, top: 10),
child: Center(
child: Column(
children: [
Row(
children: <Widget>[
RaisedButton(
child: Text('Start EventChannel',
style: TextStyle(fontSize: 12)),
onPressed: _startTimer,
),
Padding(
padding: EdgeInsets.only(left: 10),
child: RaisedButton(
child: Text('Cancel EventChannel',
style: TextStyle(fontSize: 12)),
onPressed: _stopTimer,
)),
Padding(
padding: EdgeInsets.only(left: 10),
child: Text("$_count"() [() [() [() [() [() }}Copy the code
The effect is shown below:
BasicMessageChannel
BasicMessageChannel is more like communication of a message and can be used for simple communication rather than calling a method or event flow
BasicMessageChannel can also implement bidirectional communication between Flutter and Native. The following illustration is an official example:
Native FAB
Flutter
Flutter FAB
Native
Flutter server-side code
class _MyHomePageState extends State<MyHomePage> {
static const String _channel = 'increment';
static const String _pong = 'pong';
static const String _emptyMessage = ' ';
static const BasicMessageChannel<String> platform =
BasicMessageChannel<String>(_channel, StringCodec());
int _counter = 0;
@override
void initState() { super.initState(); / / set the message processor platform. SetMessageHandler (_handlePlatformIncrement); } // If a Native message is received, the number +1 Future<String> _handlePlatformIncrement(String message) async {setState(() { _counter++; }); // Send an empty messagereturn_emptyMessage; } // Click FAB in Flutter to send messages to Native void_sendFlutterIncrement() {
platform.send(_pong);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BasicMessageChannel'),
),
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Center(
child: Text(
'Platform button tapped $_counter time${_counter == 1 ? '':'s'}., style: const TextStyle(fontSize: 17.0),),), Container(padding: const EdgeInsets. Only (bottom: 15.0, left: 5.0), child: Row(children: <Widget>[image.asset ('assets/flutter-mark-square-64.png', scale: 1.5),
const Text('Flutter', style: TextStyle(fontSize: 30.0)),],),),],)), floatingActionButton: floatingActionButton (onPressed: _sendFlutterIncrement, child: const Icon(Icons.add), ), ); }}Copy the code
Native client code
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // omit other code... messageChannel = new BasicMessageChannel<>(flutterView, CHANNEL, StringCodec.INSTANCE); messageChannel.setMessageHandler(new MessageHandler<String>() { @Override public void onMessage(String s, Reply<String> Reply) {// Receive a Flutter message and update Native onFlutterIncrement(); reply.reply(EMPTY_MESSAGE); }}); FloatingActionButton fab = findViewById(R.id.button); fab.setOnClickListener(new View.OnClickListener() {@override public void onClick(View v) {// Notify the Flutter to update sendAndroidIncrement(); }}); } private voidsendAndroidIncrement() {
messageChannel.send(PING);
}
private void onFlutterIncrement() {
counter++;
TextView textView = findViewById(R.id.button_tap);
String value = "Flutter button tapped " + counter + (counter == 1 ? " time" : " times");
textView.setText(value);
}
Copy the code
So much for the communication between Flutter and Native. In summary, you can use MethodChannel if communicating requires method calls, EventChannel if communicating with data flows, and BasicMessageChannel if communicating with just message notifications.
Reference
Flutter. Dev/docs/develo… Juejin. Cn/post / 684490… Juejin. Cn/post / 684490… Juejin. Cn/post / 684490…
To contact me
The following is my public number, dry goods articles not bad, there is a need to pay attention to, there are any questions can contact me: