-
Notifications
You must be signed in to change notification settings - Fork 1
Upgrading Flutter projects from using PlatformMessages to using channels
(This wiki page applies to people migrating code written before March 2017. It is unlikely to still be relevant.)
Prior to pull requests flutter/flutter#8837 and flutter/engine#3482, both merged on March 17 2017, communication between Flutter app components written in Dart and host platform app components written for Android or iOS was accomplished by sending and receiving string/JSON messages using methods defined in the following types:
-
PlatformMessages
(Dart) -
FlutterView
(Android) -
FlutterViewController
(iOS)
These methods have been removed, leaving only methods for sending unencoded binary messages. A new channel concept has been introduced in their place:
-
PlatformMessageChannel
,PlatformMethodChannel
(Dart) -
FlutterMessageChannel
,FlutterMethodChannel
(Android, iOS)
The main goals of the new API are to achieve
- higher-level communication: asynchronous method calls and event streams
- less boilerplate and redundancy
- a more symmetric API across Dart, Android, and iOS
The following sections detail how to port code written against the old API.
With the new API you define in one place the name and type of the channel you need:
var fooChannel = new PlatformMessageChannel<String>('foo', const StringCodec());
var barChannel = new PlatformMethodChannel('bar', const JSONMethodCodec());
There are four codecs to choose from: binary, string, JSON, and standard. The standard codec employs efficient binary serialization of JSON-like values, supporting also buffers as leaf values (e.g. Dart TypedData
, Java primitive arrays, Cocoa NSData
).
Once you have a channel, you can use it in multiple places without repeating the information needed for its creation.
Replace code like this
String reply = await PlatformMessages.sendString('foo', myString);
with code like this
String reply = await fooChannel.send(myString);
Replace code like this
PlatformMessages.setStringMessageHandler('foo', (String message) async {
// do something, then
return reply;
});
with code like this
fooChannel.setMessageHandler((String message) async {
// do something, then
return reply;
});
Replace code like this
var arguments = { 'argA': 'hello', 'argB': 42 };
var message = {
'method': 'someMethod',
'args': <Map<String, dynamic>>[arguments],
};
dynamic reply = await PlatformMessages.sendJSON('bar', message);
// what about errors?
or this
var arguments = { 'argA': 'hello', 'argB': 42 };
dynamic reply = await PlatformMessages.invokeMethod(
'bar',
'someMethod',
<Map<String, dynamic>>[arguments],
);
// what about errors?
with code like this
try {
dynamic result = await barChannel.invokeMethod(
'someMethod',
{ 'argA': 'hello', 'argB': 42 },
);
// use result
} on PlatformException catch(e) {
// handle error
}
Replace code like this
PlatformMessages.setJSONMessageHandler('bar', (dynamic methodCall) async {
String method = methodCall['method'];
List arguments = methodCall['args'];
// handle call then
return result;
// but what about errors?
});
with code like this
barChannel.setMethodCallHandler((MethodCall call) async {
String method = call.method;
dynamic arguments = call.arguments;
// handle call then
return result;
// or
throw new PlatformException(errorCode, anErrorMessage, someDetails);
});
See platform_channel for an example.
Similar to Flutter side, using FlutterMessageChannel
and FlutterMethodChannel
from io.flutter.plugin.common
.
FlutterView view = ...
FlutterMessageChannel<String> fooChannel =
new FlutterMessageChannel<>(view, "foo", StringCodec.INSTANCE);
fooChannel.send(myString);
// or if you need to handle a reply:
fooChannel.send(myString, new ReplyHandler<String>() {
public void onReply(String reply) {
// do something with reply
}
});
API documentation. See platform_channel for another example.
Similar to Flutter side, using FlutterMessageChannel
and FlutterMethodChannel
from FlutterChannels.h
.
FlutterViewController controller = ...
FlutterMessageChannel* fooChannel =
[FlutterMessageChannel messageChannelWithName:@"foo"
binaryMessenger:controller
codec:[FlutterStringCodec sharedInstance]];
[fooChannel sendMessage:myString];
// or if you need to handle a reply:
[fooChannel sendMessage:myString replyHandler:^(id reply) {
// do something with (NSString*)reply
}];
API documentation. See platform_channel for another example.
- Home of the Wiki
- Roadmap
- API Reference (stable)
- API Reference (master)
- Glossary
- Contributor Guide
- Chat on Discord
- Code of Conduct
- Issue triage reports
- Our Values
- Tree hygiene
- Issue hygiene and Triage
- Style guide for Flutter repo
- Project teams
- Contributor access
- What should I work on?
- Running and writing tests
- Release process
- Rolling Dart
- Manual Engine Roll with Breaking Commits
- Updating Material Design Fonts & Icons
- Postmortems
- Setting up the Framework development environment
- The Framework architecture
- The flutter tool
- API Docs code block generation
- Running examples
- Using the Dart analyzer
- The flutter run variants
- Test coverage for package:flutter
- Writing a golden-file test for package:flutter
- Setting up the Engine development environment
- Compiling the engine
- Debugging the engine
- Using Sanitizers with the Flutter Engine
- Testing the engine
- The Engine architecture
- Flutter's modes
- Engine disk footprint
- Comparing AOT Snapshot Sizes
- Custom Flutter engine embedders
- Custom Flutter Engine Embedding in AOT Mode
- Flutter engine operation in AOT Mode
- Engine-specific Service Protocol extensions
- Crashes
- Supporting legacy platforms
- Metal on iOS FAQ
- Engine Clang Tidy Linter
- Why we have a separate engine repo
- Reduce Flutter engine size with MLGO
- Setting up the Plugins development environment
- Setting up the Packages development environment
- Plugins and Packages repository structure
- Plugin Tests
- Contributing to Plugins and Packages
- Releasing a Plugin or Package
- Unexpected Plugins and Packages failures