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 result
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 package
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.
Setup
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_package
to 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 MainActivity
Pigeon is a definite improvement here.