Sitemap

Flutter — pigeon with build_runner

5 min readJul 25, 2023

--

In general, we don’t often work with native components in Flutter. However, there are instances where we need to call native APIs from Dart. MethodChannels are commonly used for this purpose, though it requires writing some boilerplate code for serialization/deserialization.

A fantastic solution to simplify this process is the pigeon package. It allows you to generate Java, Kotlin, Objective-C, Swift, C++, and other classes based on your Dart interface.

However, working with the pigeon package can be a bit challenging. For example, every time we make changes to the pigeon file, we must execute flutter pub run pigeon — input path/to/input.dart

Also, managing multiple pigeon files becomes cumbersome, because we can’t add external imports (except package:pigeon/pigeon.dart), limiting the sharing of configurations between pigeons.

Recognizing these difficulties, I took the initiative to create a package called pigeon_build_runner that makes using pigeons with build_runner more convenient. It also enables configuring pigeons via the pubspec.yaml file, making the process smoother.

Moreover, the package allows sharing base paths and package names among pigeons, significantly reducing boilerplate code.

The main highlight of this package is its compatibility with the watch command of build_runner. Now, any changes made to pigeons will instantly update all native-generated classes. Besides, only the modified pigeons are re-generated, resulting in improved performance.

In summary, the pigeon_build_runner package simplifies working with pigeons in Flutter, providing a more seamless and efficient experience when dealing with native APIs.

Let’s get started

Install Dependencies

First of all, we need to install pigeon, build_runner, and pigeon_build_runner as dev dependencies in your pubspec.yaml file.

dev_dependencies:
pigeon: ^<latest_pigeon_version>
build_runner: ^<latest_build_runner_version>
pigeon_build_runner: ^<latest_pigeon_build_runner_version>

Don’t forget to replace <version> in the example with the last available version in pub.dev

Include a pigeon folder

If you keep all you pigeons files inside a “lib” folder, you can skip this step and go to the next one. However, if some or all your pigeon files are located outside of the “lib” folder (as recommended in the pigeon documentation) you need including this folder in a build.yaml to make build_runner works with it.

For security purposes, build_runner, by default, restricts access to files located outside the “lib” folder. Nevertheless, there are exceptions in the form of files and folders listed in defaultRootPackageSources, which apply to packages running in the root package, and defaultNonRootVisibleAssets, which apply to non-root packages.

Fortunately, extending these lists is a simple task. To grant access to our desired files or folders, we can add their paths to the build.yaml file in the additional_public_assets property. This will ensure that our specified assets are accessible and processed appropriately.

Here’s an example of how to add a “pigeons” folder that located in the root folder of the project into the additional_public_assets:

  • Create a build.yaml file in the root folder of the project (where pubspec.yaml is located)
  • Put the example belowe in the file.
additional_public_assets:
# Add the directories containing your pigeon files here, relative to the root directory
- pigeons/**

You can add as much files or folders as you need. Also you can use patterns here.

Configure pigeon_build_runner

To set up the pigeon_build_runner package, simply add a pigeon property to your pubspec.yaml file and define the required inputs.

Here’s an actual example to illustrate this process beautifully.

pigeon:
# main-input defines default values and base paths or package names
main-input:
input: pigeons/
dart:
out: lib/src/pigeons/
test-out: test/pigeons/
ast:
out: lib/src/pigeons_ast/
objc:
header-out: ios/Runner/
source-out: ios/Runner/
# Set this to a unique prefix for your plugin or application, per Objective-C naming conventions.
prefix: PGN
java:
out: android/app/src/main/java/dev/flutter/pigeon_example_app/
package: your.package.name
swift:
out: ios/Runner/
kotlin:
out: android/app/src/main/kotlin/dev/flutter/pigeon_example_app/
package: your.package.name
cpp:
header-out: windows/runner/
source-out: windows/runner/
namespace: CppNamespace
inputs:
- input: my_pigeon.dart
dart:
out: my_pigeon.dart
test-out: my_pigeon_test.dart
ast:
out: my_pigeon_ast.dart
objc:
header-out: my_pigeon.g.h
source-out: my_pigeon.g.s
java:
out: MyPigeon.g.java
# Adding `.` before a package will join a package name from main-input with the package belowe
# Result: your.package.name.additional.package.name
package: .additional.package.name
kotlin:
out: MyPigeon.g.kt
# Adding `.` before a package will join a package name from main-input with the package belowe
# Result: your.package.name.additional.package.name
package: .additional.package.name
swift:
out: MyPigeon.g.swift
cpp:
header-out: my_pigeon.g.h
source-out: my_pigeon.g.cpp

main-input

It’s a place where you can define base or defalut values for all inputs you have.

There are two kinds of properties:

Combinable: These properties encompass all paths (input paths or any properties with an out suffix) and can be easily combined. This means you can link the path defined in the main-input with a path in the subsequent inputs. To achieve this, simply set a relative path in the inputs section, either without or with a “./” prefix. But you alwas can override a path with an absolute one just by adding “/” at a start of the path in any inputs.

Additionally, even package properties can be combined. To join a package property from the main-input with an input’s property, just append a dot “.” before the package value in the inputs section. If you would like fully override a package name, just don’t add a dot as a prefix.
This seamless combination allows for efficient handling of various configurations and facilitates a streamlined setup process.

Overridable: The following properties, copyright-header, one-language, debug-generators, prefix, and use-generated-annotation, can be defined in the main-input and will be applied as default settings for all inputs.

However, users have the flexibility to override these settings for individual inputs, allowing for greater customization and adaptability.

inputs

This is a list of all pigeon files you would like to generate to. This property maintains the same structure as the main-input, with the distinction that you must specify a file name in all input/output paths.
Feel free to add as much pigeons as you need here and don’t worry about performance, build_runner will generate only pigeons you change.

Conclusion

In conclusion, the pigeon_build_runner and pigeon packages have significantly improved Flutter development by simplifying the integration of native components and APIs. The pigeon package streamlined code generation for native platforms based on Dart interfaces, and pigeon_build_runner enhanced the process further, allowing for smoother iterations and faster development cycles. With these powerful tools, Flutter developers can efficiently leverage native functionalities, creating robust and feature-rich applications with ease.

pub.dev: https://pub.dev/packages/pigeon_build_runner

Author: Kirill Liubimov
Website: https://lkirill.com
LinkedIn: https://www.linkedin.com/in/kirill-lyubimov-06a68712b/
GitHub: https://github.com/rotorgames
Twitter: https://twitter.com/kirillrg

--

--

Responses (1)