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…

Platform side
Flutter side

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.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store