Pigeon
Learn how to use Pigeon to generate type-safe bindings between your Dart code and the host platform.
Overview
#Pigeon is the recommended default tool for building Flutter plugins. Pigeon replaces the manual setup of raw platform channels with generated, type-safe APIs. This helps prevent typos in method names or mismatched argument types.
Choose a communication option with Pigeon
#Pigeon supports the following two communication options depending on your target platform and performance needs: native interop (direct FFI and JNI) or platform channels.
Pigeon with native interop
#Pigeon with native interop uses FFIgen and JNIgen under the hood to generate direct native bindings (using FFI on iOS and macOS, and JNI on Android).
Use this option if your use case requires any of the following:
- You need synchronous execution on your native platform.
- You're doing high-frequency data streaming (for example, audio or camera frames).
- You're calling native code from background Dart isolates.
Pigeon with platform channels
#Pigeon with platform channels generates standard platform channels that support Android, iOS, macOS, Windows, and Linux.
Use this option if your use case requires any of the following:
- You need desktop support on Windows or Linux.
- You want zero external toolchains.
Developer flow
#Generate type-safe bindings with Pigeon using the following steps:
- Create the Pigeon definition file.
- Generate the code.
- Implement the native interface.
- Call the API from Flutter.
To start, create a Pigeon definition file in Dart
(typically outside of your lib/ directory, such as pigeons/messages.dart).
This file defines the data structures and the API interfaces
using Pigeon annotations like @HostApi and @FlutterApi.
Next, run the Pigeon generator tool, which parses your Pigeon definition file
and produces the necessary code for both Dart and your target native platform.
After the code is generated, implement the generated native interfaces
in the platform-specific directories of your Flutter project
(such as android/, ios/, or macos/)
and register them with the Flutter engine.
Finally, call the generated Dart classes from your Flutter app
(using <MyApi>.createWithNativeInteropApi() when using native interop),
or invoke the generated native APIs to call back into your Dart code.
If you need to modify the API, first modify the Pigeon definition file, regenerate the code, and then update your implementation accordingly.
Examples
#To explore a complete implementation of this flow, including the Pigeon definition file, generated code, and native implementations, refer to the following examples:
- Example of Pigeon with native interop
- Example of Pigeon with platform channels
-
shared_preferencesplugin
Constraints and limitations
#Keep the following constraints and limitations in mind when working with Pigeon:
High-frequency data
#For specialized use cases requiring direct manipulation of raw native pointers or C and C++ memory management, use FFIgen or JNIgen instead.
Inheritance
#Pigeon doesn't support complex inheritance. Flatten your data models where possible.
(Pigeon with platform channels only) Async errors
#
In Kotlin and Swift, @async generates suspend and async throws
functions where native code throws FlutterError or PigeonError
directly.
For @asyncCallback methods—or when using the Java, Objective-C, C++,
or GObject generators—native code must return errors through the provided
completion callback.
Resources
#To learn about building and migrating plugins with Pigeon, refer to the following resources:
Unless stated otherwise, the documentation on this site reflects Flutter 3.47. Page last updated on 2026-11-03. View source or report an issue.