Overview for Communicate Between Flutter and Native Code Using Platform Channel

Mohamed Abdo Elnashar
Flutter UAE
Published in
7 min readJun 12, 2023

flutter with native code

from the Internet

In this article, we will explore the concept of platform channels in Flutter and demonstrate how to communicate between Flutter and native code using these channels. This can be particularly useful when you need to access native platform-specific features or APIs that are not yet available in Flutter packages.

What is a Platform Channel?

A platform channel is a communication mechanism that allows Flutter code to send and receive messages to and from native code in a platform-specific way. Flutter uses the MethodChannel class to establish a communication channel with the native platform, enabling bi-directional communication between the Dart and native code.

Why Use Platform Channels?

While Flutter offers a comprehensive set of widgets and libraries for building cross-platform applications, there may be instances where you need to access platform-specific features or APIs that still need to be supported by existing Flutter packages. In these cases, platform channels allow you to bridge the gap between Flutter and native code, enabling you to utilize platform-specific functionalities within your Flutter app.

architectural overview from the Internet

When a Flutter app needs to communicate with the native code, it sends messages over a platform channel. The host, which represents the native platform, listens on the platform channel and receives these messages. It then executes the corresponding native code based on the received message and sends a response back to the Flutter code.

It’s worth noting that all messages sent over the platform channel are encoded into binary before being transmitted, ensuring efficient and secure communication. Similarly, any binary results received from the host are decoded back into Dart values for easy consumption within the Flutter app.

These are the three types of platform channels APIs offered by Flutter:

BasicMessageChannel:

  • This channel is used for asynchronous message passing with platform plugins.
  • It takes a MessageCodec that encodes and decodes the data being passed.
  • Flutter provides four types of codecs: StringCodec, BinaryCodec, JSONMessageCodec, and StandardMessageCodec.
final messageChannel = BasicMessageChannel<String>('platform.testing/sensor_support', StringCodec());

MethodChannel:

  • This channel is used for asynchronous method calls with platform plugins.
  • It allows executing a function in the native code and getting a result back.
  • This is the most commonly used platform channel that suits many use cases.
final methodChannel = MethodChannel('platform.testing/method_calls');

EventChannel:

  • This channel is used for communicating with platform plugins using event streams.
  • It is suitable for listening to changes in the native code and updating your Dart stream whenever a change occurs.
final eventChannel = EventChannel('platform.testing/event_stream');

These platform channels provide a way for Flutter to interact with platform-specific code, such as accessing device features or integrating with native libraries. You can choose the appropriate channel depending on your specific needs and the type of communication required with the platform.

Setting Up a Platform Channel

To set up a platform channel, we need to perform the following steps:

  1. Choose a unique channel name.
  2. Create a MethodChannel object with the chosen channel name in both Dart and native code.
  3. Implement the message handlers in both Dart and native code.

Let’s walk through these steps with an example.

Step 1:Choose a Unique Channel Name

A channel name is a string that uniquely identifies a platform channel. It is used to establish communication between Dart and native code. It’s essential to choose a unique name to avoid conflicts with other platform channels.

const platformChannel = MethodChannel('com.example.platform_channel_in_flutter/tes');

Step 2: Create a MethodChannel Object

In the Flutter code, import the services library and create a MethodChannel object with the chosen channel name:

import 'package:flutter/services.dart';

const platform =
MethodChannel('com.example.platform_channel_in_flutter/test');

In the native code, create a corresponding MethodChannel object with the same channel name.

In Dart, you can define this interface using a class that extends the MethodChannel class from the package:flutter/services package. For example, let's say you want to access a native method that retrieves the current device model:

import 'package:flutter/services.dart';

class DeviceModel {
static const platform =
MethodChannel('com.example.platform_channel_in_flutter/test');
static Future<String> getDeviceModel() async {
try {
final result = await platform.invokeMethod('getDeviceModel');
return result;
} catch (e) {
return '';
}
}
}

In the above code snippet, we define a DeviceModel class that uses a MethodChannel named 'com.example.platform_channel_in_flutter/test' to communicate with the native code. The getDeviceModel() method invokes the native method 'getDeviceModel' using platform.invokeMethod(). It returns the device model as an string or ‘’ in case of any error.

Step 3: Implement Message Handlers

In both the Dart and native code, implement the message handlers to handle incoming messages.

The binaryMessengeris employed for transferring binary data to and from the native platform, while the codes is responsible for converting and interpreting these values through serialization and deserialization.

If no value is specified in this particular area, the default behavior is to utilize the StandardMethodCodec. Similarly, if we do not provide any specific input, the defaultBinaryMessenger is automatically used.

In native code For Android (Kotlin)

On the native side, you need to implement the method that will be invoked by Flutter. In Android, you can create a method in your MainActivity class that retrieves the device model:

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
// Channel name (for example package_name/app_name)
private val CHANNEL = "com.example.platform_channel_in_flutter/test"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
if (call.method == "getDeviceModel") {
val deviceModel = getDeviceType(context)
result.success(deviceModel)
} else {
// if called undefined method
result.notImplemented()
}
}
}
private fun getDeviceType(context: Context): String {
// Add your native code here to retrieve the device model
return ''; // A sample value
}
}

In the Android code above, we create a new MethodChannel one with the same channel name 'com.example.platform_channel_in_flutter/test' as defined in Dart. We set a MethodCallHandler to handle incoming method calls from Flutter. In this example, we check if the method name is 'getDeviceModel' and then call the getDeviceModel() method to retrieve the device model. Finally, we send the result back to Flutter using result.success().

Android Studio screenshot

In native code For iOS (Swift)

For iOS, the process is similar. You can create a method in your AppDelegate class to handle the method call from Flutter

The AppDelegate class is marked with the @UIApplicationMain annotation, serving as the starting point of the program's execution. In essence, it can be seen as the equivalent of the void main() function in Flutter, as it initiates the execution of the application :

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {

let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let deviceChannel = FlutterMethodChannel(name: "com.example.platform_channel_in_flutter/test",
binaryMessenger: controller.binaryMessenger)
prepareMethodHandler(deviceChannel: deviceChannel)
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

private func prepareMethodHandler(deviceChannel: FlutterMethodChannel) {
deviceChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if call.method == "getDeviceModel" {

self.receiveDeviceModel(result: result)
}
else {
result(FlutterMethodNotImplemented)
return
}

})
}

private func receiveDeviceModel(result: FlutterResult) {
let deviceModel = UIDevice.current.model
result(deviceModel)
}
}

In the iOS code above, we create a FlutterMethodChannel with the same channel name 'com.example.platform_channel_in_flutter/test' as defined in Dart. We set a setMethodCallHandler to handle incoming method calls from Flutter. Similarly to the Android example, we check if the method name is 'getDeviceModel' and call the getDeviceModel() method to retrieve the device model. Finally, we send the result back to Flutter using result().

Xcode screenshot

Dart output

the output of the execution code

With the platform interface defined in Dart and the corresponding method implemented in the native code, you can now easily communicate between Flutter and native code using the Platform Channel. You can extend this concept to invoke complex operations, pass arguments between Flutter and native code, and handle platform-specific callbacks.

Remember to handle exceptions gracefully and provide fallbacks in case the native method is not available or encounters an error. Proper error handling ensures that your Flutter app remains robust and stable across different platforms.

I hope you all liked this blog and it helped you start with Flutter! Don’t forget to smash that clap button and leave a comment down below.

If you liked this article make sure to 👏 it below, and connect with me on Portfolio, Github, and LinkedIn.

Meet you at the next one.

--

--

Mohamed Abdo Elnashar
Flutter UAE

Senior Flutter Developer, and I study a master of computer science in the faculty of computer & information sciences at Mansoura university