Flutter to Native: The Secret Sauce of Platform Channels (with Pizza!)


In the magical land of Flutter, everything feels perfect. Widgets flutter, animations flow, and UIs look crisp across devices. But there’s one small problem…
Flutter can’t talk to native Android or iOS code directly.
It’s like being in a group chat where everyone else speaks Kotlin or Swift, and Flutter’s there like, “Anyone speak Dart?” No response. Just confused stares.
But Flutter, being the clever creature it is, came up with a brilliant idea—Platform Channels—the secret tunnel that lets Dart code talk to native code like old friends sharing pizza.
What is a Platform Channel?
Imagine Flutter and Native (Android/iOS) living in separate houses across the street. They don’t speak the same language, but they can pass messages through paper airplanes.
Those paper airplanes are Platform Channels—a way for Flutter to send and receive messages to and from native code.
There are three kinds of messengers in this system:
MethodChannel – The one-time task messenger
BasicMessageChannel – The casual chatty buddy
EventChannel – The non-stop DJ streaming updates from native to Flutter
Let’s meet them.
MethodChannel – The Task Doer
Think of MethodChannel as a personal assistant. You give a task, and expect a result.
Flutter: “Hey Android, what’s the battery level?”
Android: “Checking... it’s 78%.”
Flutter: “Thanks, buddy.”
That’s MethodChannel. You ask once, get an answer, done.
Flutter (Dart):
const platform = MethodChannel('samples.flutter.dev/battery');
Future<void> getBatteryLevel() async {
try {
final int result = await platform.invokeMethod('getBatteryLevel');
print('Battery is $result%.');
} catch (e) {
print('Error: $e');
}
}
Android (Kotlin):
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "samples.flutter.dev/battery")
.setMethodCallHandler { call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
result.success(batteryLevel)
} else {
result.notImplemented()
}
}
Use this when Flutter needs to say:
"Do this once and let me know."
BasicMessageChannel – The Chill Conversationalist
BasicMessageChannel is more like two buddies texting each other casually.
Flutter: “Hey, just checking in!”
Android: “All good here. You?”
Flutter: “Living the widget life.”
It’s perfect for flexible, bi-directional messaging without commands.
Flutter (Dart):
const channel = BasicMessageChannel<String>(
'samples.flutter.dev/message', StringCodec());
void sendMessage() {
channel.send('Hello Native!');
}
void receiveMessages() {
channel.setMessageHandler((message) async {
print('From Native: $message');
return 'Message received!';
});
}
Android (Kotlin):
channel = BasicMessageChannel(
flutterEngine.dartExecutor.binaryMessenger,
"samples.flutter.dev/message",
StringCodec.INSTANCE
)
channel.setMessageHandler { message, reply ->
println("Flutter says: $message")
reply.reply("Hey Flutter, I got your message!")
}
Use this when you need ongoing communication and don’t want the pressure of a command–response format.
EventChannel – The Party DJ
Now meet EventChannel—Flutter’s connection to the non-stop stream of updates.
It’s like a DJ at a party who keeps sending beats to the dancefloor (Flutter). Once Flutter tunes in, the music (data) flows continuously until someone pulls the plug.
Flutter: “Hey DJ Native, start the stream!”
Native: “Here comes the vibe… 98 BPM, 99 BPM, 100 BPM…”
Flutter (Dart):
const eventChannel = EventChannel('samples.flutter.dev/sensor');
void listenToSensor() {
eventChannel.receiveBroadcastStream().listen((event) {
print('Sensor update: $event');
}, onError: (error) {
print('Error: $error');
});
}
Android (Kotlin):
EventChannel(flutterEngine.dartExecutor.binaryMessenger, "samples.flutter.dev/sensor")
.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
startSendingSensorData(events)
}
override fun onCancel(arguments: Any?) {
stopSensorUpdates()
}
})
Use this when native code needs to continuously push data to Flutter: sensor readings, location updates, audio level, etc.
Pizza Analogy Time!
Let’s say Flutter and Native are trying to decide dinner plans:
MethodChannel: “Can you order a pizza?” → One request, one response
BasicMessageChannel: “I’m thinking pizza. You?” → Chill chat, both talk freely
EventChannel: “Start the pizza livestream!” → A constant stream of pizza updates until you say stop
Delicious, right?
Quick Comparison Table
Channel Type | Acts Like... | Purpose | Direction |
MethodChannel | Task request | One-time command & response | Bi-directional |
BasicMessageChannel | Text conversation | Flexible, two-way data exchange | Bi-directional |
EventChannel | Party DJ | Streaming continuous updates | Native → Flutter |
Conclusion
Platform Channels are Flutter’s passport to the native world. Whether you’re trying to open the camera, stream gyroscope data, or simply get the battery level—there’s a channel for that.
Use MethodChannel for quick commands
Use BasicMessageChannel for free-flow messages
Use EventChannel when the data never stops coming
With these tools in your pocket, you can build apps that not only look beautiful but also do powerful things behind the scenes.
So the next time you’re building with Flutter and need to speak to the native side—just pass a note through the tunnel.
And maybe order some pizza while you're at it.
Subscribe to my newsletter
Read articles from Anmol Singh Tuteja directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
