Pigeon is a code generator tool to make communication between Flutter and the host platform type-safe, easier and faster.
Currently Pigeon supports generating:
- Kotlin and Java code for Android,
- Swift and Objective-C code for iOS
- Swift code for macOS
- C++ code for Windows
Pigeon generates all the code that is needed to communicate between Flutter and the host platform, there is no extra runtime requirement. A plugin author doesn't need to worry about conflicting versions of Pigeon.
- Add Pigeon as a
dev_dependency
. - Make a ".dart" file outside of your "lib" directory for defining the communication interface.
- Run pigeon on your ".dart" file to generate the required Dart and
host-language code:
flutter pub get
thenflutter pub run pigeon
with suitable arguments (see example). - Add the generated Dart code to
./lib
for compilation. - Implement the host-language code and add it to your build (see below).
- Call the generated Dart methods.
- Add the generated Objective-C or Swift code to your Xcode project for compilation
(e.g.
ios/Runner.xcworkspace
or.podspec
). - Implement the generated protocol for handling the calls on iOS, set it up as the handler for the messages.
- Add the generated Java or Kotlin code to your
./android/app/src/main/java
directory for compilation. - Implement the generated Java or Kotlin interface for handling the calls on Android, set it up as the handler for the messages.
- Add the generated C++ code to your
./windows
directory for compilation, and to yourwindows/CMakeLists.txt
file. - Implement the generated C++ abstract class for handling the calls on Windows, set it up as the handler for the messages.
- Add the generated Objective-C or Swift code to your Xcode project for compilation
(e.g.
macos/Runner.xcworkspace
or.podspec
). - Implement the generated protocol for handling the calls on macOS, set it up as the handler for the messages.
Flutter also supports calling in the opposite direction. The steps are similar
but reversed. For more information look at the annotation @FlutterApi()
which
denotes APIs that live in Flutter but are invoked from the host platform.
- The file should contain no method or function definitions, only declarations.
- Custom classes used by APIs are defined as classes with fields of the supported datatypes (see the supported Datatypes section).
- APIs should be defined as an
abstract class
with eitherHostApi()
orFlutterApi()
as metadata. The former being for procedures that are defined on the host platform and the latter for procedures that are defined in Dart. - Method declarations on the API classes should have arguments and a return
value whose types are defined in the file, are supported datatypes, or are
void
. - Generics are supported, but can currently only be used with nullable types
(example:
List<int?>
).
Pigeon uses the StandardMessageCodec
so it supports any datatype Platform
Channels supports
[documentation].
Nested datatypes are supported, too.
By default Pigeon will generate synchronous handlers for messages and asynchronous methods. If you want a handler to be able to respond to a message asynchronously you can use the @async annotation as of version 0.1.20.
Example:
class Value {
int? number;
}
@HostApi()
abstract class Api2Host {
@async
Value calculate(Value value);
}
Generates:
// Objective-C
@protocol Api2Host
-(void)calculate:(nullable Value *)input
completion:(void(^)(Value *_Nullable, FlutterError *_Nullable))completion;
@end
// Swift
/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
protocol Api2Host {
func calculate(value: Value, completion: @escaping (Value) -> Void)
}
// Java
public interface Result<T> {
void success(T result);
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
public interface Api2Host {
void calculate(Value arg, Result<Value> result);
}
// Kotlin
/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
interface Api2Host {
fun calculate(value: Value, callback: (Result<Value>) -> Unit)
}
// C++
/** Generated class from Pigeon that represents a handler of messages from Flutter.*/
class Api2Host {
public:
virtual void calculate(Value value, flutter::MessageReply<Value> result) = 0;
}
Pigeon supports generating null-safe code, but it doesn't yet support:
- Nullable generics type arguments
- Nullable enum arguments to methods
Pigeon supports enum generation in class fields. For example:
enum State {
pending,
success,
error,
}
class StateResult {
String? errorMessage;
State? state;
}
@HostApi()
abstract class Api {
StateResult queryState();
}
Prior to version 1.0 all arguments to API methods had to be wrapped in a class, now they can be used directly. For example:
@HostApi()
abstract class Api {
Map<String?, int?> makeMap(List<String?> keys, List<String?> values);
}
When targeting a Flutter version that supports the
TaskQueue API
the threading model for handling HostApi methods can be selected with the
TaskQueue
annotation:
@HostApi()
abstract class Api2Host {
@TaskQueue(type: TaskQueueType.serialBackgroundThread)
int add(int x, int y);
}
All Host API exceptions are translated into Flutter PlatformException
.
- For synchronous methods, thrown exceptions will be caught and translated.
- For asynchronous methods, there is no default exception handling; errors should be returned via the provided callback.
To pass custom details into PlatformException
for error handling, use FlutterError
in your Host API.
For example:
// Kotlin
class MyApi : GeneratedApi {
// For synchronous methods
override fun doSomething() {
throw FlutterError('error_code', 'message', 'details')
}
// For async methods
override fun doSomethingAsync(callback: (Result<Unit>) -> Unit) {
callback(Result.failure(FlutterError('error_code', 'message', 'details'))
}
}
Likewise, Host API errors can be sent using the provided FlutterError
class (translated into PlatformException
).
For synchronous methods:
- Objective-C - Assign the
error
argument to aFlutterError
reference. - C++ - Return a
FlutterError
directly (for void methods) or within anErrorOr
instance.
For async methods:
- Both - Return a
FlutterError
through the provided callback.
Then you can implement error handling on the Flutter side:
// Dart
void doSomething() {
try {
myApi.doSomething()
} catch (PlatformException e) {
if (e.code == 'error_code') {
// Perform custom error handling
assert(e.message == 'message')
assert(e.details == 'details')
}
}
}
File an issue in flutter/flutter with the word "pigeon" in the title.