preface
As a mixed development of Flutter, some interaction with the native terminal is inevitable. For example, when the native system sensor is called and the network framework of the native terminal is used for data request, the method that Flutter calls Android and Android calls Flutter is used. Platform Channels
Platform Channels
A Flutter passes messages between a Channel and a client, as shown in the following figure:
This is a MethodChannel for message passing between a Flutter and a client. MethodChannel is one of the Platform Channels. A Flutter has three types of communication:
Method Invocation BasicMessageChannel: Method Invocation usually used to call a method in native invocation EventChannel: Communication for Event Streams. There are monitoring functions, such as power changes that push data directly to the flutter terminal.Copy the code
To ensure that the UI responds, messages passed through Platform Channels are asynchronous.
For more information on channel principles, see this article: Channel Principles
Platform Channels to use
1. MethodChannel use
Native client writing (Using Android as an example)
Start by defining a way to get your phone’s battery
private int getBatteryLevel() {
return 90;
}
Copy the code
This function is the method to be called on Flutter, and you need to set up this MethodChannel with MethodChannel.
Let’s start with a new method that initializes a MethodChannel
private String METHOD_CHANNEL = "common.flutter/battery";
private String GET_BATTERY_LEVEL = "getBatteryLevel";
private MethodChannel methodChannel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
initMethodChannel();
getFlutterView().postDelayed(() ->
methodChannel.invokeMethod("get_message", null, new MethodChannel.Result() {
@Override
public void success(@Nullable Object o) {
Log.d(TAG, "get_message:" + o.toString());
}
@Override
public void error(String s, @Nullable String s1, @Nullable Object o) {
}
@Override
public void notImplemented() {}}), 5000); } private voidinitMethodChannel() {
methodChannel = new MethodChannel(getFlutterView(), METHOD_CHANNEL);
methodChannel.setMethodCallHandler(
(methodCall, result) -> {
if (methodCall.method.equals(GET_BATTERY_LEVEL)) {
int batteryLevel = getBatteryLevel();
if(batteryLevel ! = -1) { result.success(batteryLevel); }else {
result.error("UNAVAILABLE"."Battery level not available.", null); }}else{ result.notImplemented(); }}); } private intgetBatteryLevel() {
return 90;
}
Copy the code
Method_channels are used to identify interactions with a flutter. Since there are usually multiple channels, they need to be unique within the app
Methodchannels are stored in a Map with the channel name Key. So if you set two channels with the same name, only the latter one will take effect.
OnMethodCall takes two parameters, and the onMethodCall contains the name of the method to call and its parameters. Result is the return value to Flutter. The method name is set by the client and the Flutter. Methodcall. method is distinguished by the if/switch statement, and in our case we will only handle calls called “getBatteryLevel”. The charge value is returned to the Flutter using the result.success(batteryLevel) call after the local method is retrieved.
MethodChannel – Flutter
Take a look at the code on the Flutter side first
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
static const platform = const MethodChannel('common.flutter/battery');
void _incrementCounter() {
setState(() {
_counter++;
_getBatteryLevel();
});
}
@override
Widget build(BuildContext context) {
platform.setMethodCallHandler(platformCallHandler);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
Text('$_batteryLevel'),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
String _batteryLevel = 'Unknown battery level.';
Future<Null> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() { _batteryLevel = batteryLevel; }); } // Future<dynamic> platformCallHandler(MethodCall call) async {switch (call.method) {case "get_message":
return "Hello from Flutter";
break; }}}Copy the code
First, define a constant result.success(platform), which is the same as the channel defined by the Android client. Next, define a result.success(_getBatteryLevel()) method to call the Android method, result.success(final int result = await platform.invokeMethod(‘getBatteryLevel’);) This line of code calls Native (Android) methods via channels. Because MethodChannel is called asynchronously, you must use the await keyword here.
In the Android code above we pass result.success(batteryLevel); Return to Flutter. Here the power is assigned directly to the result variable after execution of the await expression. Result.success (setState); To change the Text display value. Up to this point, native client methods are invoked through the Flutter side.
MethodChannel is a method that can be called both ways. In the code above, we have also shown that a native client calls a Flutter method.
In the primary side through methodChannel. InvokeMethod method call
methodChannel.invokeMethod("get_message", null, new MethodChannel.Result() {
@Override
public void success(@Nullable Object o) {
Log.d(TAG, "get_message:" + o.toString());
}
@Override
public void error(String s, @Nullable String s1, @Nullable Object o) {
}
@Override
public void notImplemented() {}});Copy the code
On the Flutter side you need to set a MethodCallHandler for the MethodChannel
static const platform = const MethodChannel('common.flutter/battery');
platform.setMethodCallHandler(platformCallHandler);
Future<dynamic> platformCallHandler(MethodCall call) async {
switch (call.method) {
case "get_message":
return "Hello from Flutter";
break; }}Copy the code
So that’s the relevant use of MethodChannel.
EventChannel
The data will be pushed to the Flutter, similar to the usual push function. If necessary, the Flutter will be pushed to the Flutter. It is up to the Flutter side to decide whether to process the push. In contrast to MethodChannel, which is actively fetched, EventChannel is passively pushed.
EventChannel native client
private String EVENT_CHANNEL = "common.flutter/message";
private int count = 0;
private Timer timer;
private void initEventChannel() {
new EventChannel(getFlutterView(), EVENT_CHANNEL).setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
timer.schedule(new TimerTask() {
@Override
public void run() {
if (count < 10) {
count++;
events.success("Current time :" + System.currentTimeMillis());
} else{ timer.cancel(); }}}, 1000, 1000); } @Override public void onCancel(Object o) { } }); }Copy the code
In the code above, we create a timer that pushes a message to the Flutter every second, telling the Flutter the current time. In order to prevent continuous countdown, I made a count on my side and stopped sending after 10 times.
The EventChannel Flutter end
String message = "not message";
static const eventChannel = const EventChannel('common.flutter/message');
@override
void initState() {
super.initState();
eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
}
void _onEvent(Object event) {
setState(() {
message =
"message: $event";
});
}
void _onError(Object error) {
setState(() {
message = 'message: unknown.';
});
}
Copy the code
The code above shows that the Flutter side receives native client data by _onEvent and displays the data as Text. This implementation is relatively simple. In order to achieve business classification, data needs to be packaged into JSON, and corresponding business identifiers and data can be packaged through JSON data to distinguish.
BasicMessageChannel
BasicMessageChannel (mainly passing strings and some semi-structured data)
BasicMessageChannel Android end
private void initBasicMessageChannel() { BasicMessageChannel<Object> basicMessageChannel = new BasicMessageChannel<>(getFlutterView(), BASIC_CHANNEL, StandardMessageCodec.INSTANCE); Basicmessagechannel.send (basicMessagechannel.send ("send basic message", (object)-> {
Log.e(TAG, "receive reply msg from flutter:"+ object.toString()); }); . / / receiving flutter message and send reply basicMessageChannel setMessageHandler ((object, reply) - > {the e (the TAG,"receive msg from flutter:" + object.toString());
reply.reply("Reply: Got your message");
});
}
Copy the code
BasicMessageChannel Flutter end
static const basicChannel = const BasicMessageChannel('common.flutter/basic', StandardMessageCodec()); Future<String> sendMessage() async {String reply = await basicChannel.send()'this is flutter');
print("receive reply msg from native:$reply");
returnreply; } / / receive the original message And send the reply to void receiveMessage () async {basicChannel. SetMessageHandler ((MSG) async {print("receive from Android:$msg");
return "get native message";
});
Copy the code
The codec used in the above example is StandardMessageCodec. In this example, the communication is always String, but StringCodec will also work.
Flutter provides three platforms and dart message communication modes.