Flutter — Native communication with Pigeon
Calling from Flutter back down to the native platform (iOS / Android) has always been possible in Flutter, and pretty straightforward. But, it’s always been a bit loose.
How it’s been…
Whats happening here?
From the Flutter side, we get the
MethodChannel and invoke a method. As you can see there is room for error, as the method or channel could be miss typed.
Also, the result of this call is
Any .. so at that point unless we know the native part, we have no idea whats coming back. When we do know what to expect we then have to cast the result to be what we want.
From the platform side, we initialise the channel and set a method handler. Inside there we listen for a method name, and act on it, passing any return value back into the
Basically, it works, but it’s not safe.
Catch the Pigeon
As if a man of my advancing years could write something about a library called Pigeon and not mention the classic 🎵🎵 Catch the Pigeon 🎵🎵
Pigeon is a package created and maintained by Google. It’s a code generating library to make communication with the native platform faster, and type safe.
Documentation for this package seems a little lacking. Here’s what I’ve pieced together.
Add the dependency to your pubspec.yml file, as a dev dependency:
As Pigeon is a dev dependency, create a folder outside the lib folder to store our pigeons, and create a
.dart file in there like this:
In my messaging file I created and Api and a data class:
Marking the class with
@HostApi tells the code generator what to act on.
Once we’ve made changes we have to run the code generator:
flutter pub run pigeon \--input pigeons/messaging.dart \--dart_out lib/pigeon.dart \--objc_header_out ios/Runner/pigeon.h \--objc_source_out ios/Runner/pigeon.m \--java_out ./android/app/src/main/java/dev/tonyowen/pigeon/Pigeon.java \--java_package "dev.tonyowen.pigeon"
A few things about this:
- You need to make sure the java out directory exists
- You’ll need to change the
java_packageto match the package in the android app.
If that completes successfully, you’ll have generated code in the android and ios apps.
So lets use it
I created a small app, clicking a button calls this:
With this now, we’re not typing the channel, or the method. Instead calling an Api we defined, and getting back a
List<MyMessage> no casting involved.
Onto the native side
I’m using Android, as I really don’t want to be in Xcode this morning 😕 It’s practically the same though.
First, create the Api:
Here we are implementing the generated codes interface. So we are forced to provide an implementation of the
getMessages method. Also generated was the
MyMessage data class. In this example I used a synchronous method, but you can mark methods with
@async in flutter and you’ll then get a callback to use.
All that’s left to do is initialise the Api in the
Pigeon is a definite improvement here.