Invoking native iOS code from Flutter using MethodChannels
During your Flutter development, you might come across this situation when you’ll have to communicate with native platform APIs. If there is already a package on pub.dev, you just got lucky but if there is no package available you’ll have to get into this communication between Flutter and native code.
Flutter uses a very flexible system to call platform-specific APIs. In this article, we’ll be setting up a communication channel between Flutter and iOS code. We’ll be fetching the current badge count on the App Icon. On the native side, this can be done by the following one-liner.
UIApplication.shared.applicationIconBadgeNumber
Our native iOS app will keep listening to a message during the app’s lifecycle. From our futter app we will send a message on this channel. The native listener will now catch hold of this message and will return the response after verifying the message details.
Let’s begin! Make sure to check out the article for setting up push notifications in flutter and the article for simulating push notifications on iOS simulators for playing around with badge counts.
In your widget let’s first create a channel for communication.
static const platform = const MethodChannel('method_channel.flutter.dev/appIconBadge');
The native and flutter sides of the channel are connected through a channel name passed in the constructor. For our example, we have given channel name method_channel.flutter.dev/appIconBadge.
Now we’ll be sending a message to the native side of the channel.
final int result = await platform.invokeMethod('getBadgeCount');
Now the name of the message getBadgeCount
plays a very important role. If the native side has not implemented support for this message, the Flutter host will receive a PlatformExeption
. Therefore it is advisable to use try-catch
block while invoking to avoid crashes.
String badgeCount;
try {
final int result = await platform.invokeMethod('getBadgeCount');
badgeCount = 'Badge count: $result';
} on PlatformException catch (e) {
badgeCount = "Failed to get badge count: '${e.message}'."; }
Let’s get into the native side code. Open AppDelegate.swift
file present inside ios/Runner
path.
In didFinishLaunchingWithOptions
method, we’ll be adding a listener for method_channel.flutter.dev/appIconBadge
channel.
let controller : FlutterViewController = window?.rootViewController as! FlutterViewControllerlet badgeChannel = FlutterMethodChannel(name: "method_channel.flutter.dev/appIconBadge", binaryMessenger: controller.binaryMessenger)
Now we’ll be adding the callback handler which will get executed whenever we callinvokeMethod
from the flutter host.
badgeChannel.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// Note: this method is invoked on the UI thread.
guard call.method == "getBadgeCount" else {
result(FlutterMethodNotImplemented)
return
}
self?.receiveBadgeCount(result: result)
})
Here we first check that the message if the name of the message isgetBageCount
or not. If it is something else we will return FlutterMethodNotImplemented.
Once we have guarded our check, we will execute the desired function.
func receiveBadgeCount(result: FlutterResult) -> Void {
result(UIApplication.shared.applicationIconBadgeNumber)
}
Once you’ll run your application, your flutter app and the native app will be communicating successfully.
You can find the code on Github. To test out different badge counts change the badge count in SamplePush.apns
file and drag-drop it on the simulator. To learn about its functioning check out this article.
Here is a sneak peek.
Having done this, check out the article to publish a package based on the above-implemented functionality so that others can also use it.
Happy Coding!!