| # Pigeon |
| |
| Pigeon is a code generator tool to make communication between Flutter and the |
| host platform type-safe, easier and faster. |
| |
| ## Supported Platforms |
| |
| 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 |
| |
| ## Runtime Requirements |
| |
| 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. |
| |
| ## Usage |
| |
| 1) Add Pigeon as a `dev_dependency`. |
| 1) Make a ".dart" file outside of your "lib" directory for defining the |
| communication interface. |
| 1) Run pigeon on your ".dart" file to generate the required Dart and |
| host-language code: `flutter pub get` then `flutter pub run pigeon` |
| with suitable arguments (see [example](./example)). |
| 1) Add the generated Dart code to `./lib` for compilation. |
| 1) Implement the host-language code and add it to your build (see below). |
| 1) Call the generated Dart methods. |
| |
| ### Flutter calling into iOS steps |
| |
| 1) Add the generated Objective-C or Swift code to your Xcode project for compilation |
| (e.g. `ios/Runner.xcworkspace` or `.podspec`). |
| 1) Implement the generated protocol for handling the calls on iOS, set it up |
| as the handler for the messages. |
| |
| ### Flutter calling into Android Steps |
| |
| 1) Add the generated Java or Kotlin code to your `./android/app/src/main/java` directory |
| for compilation. |
| 1) Implement the generated Java or Kotlin interface for handling the calls on Android, set |
| it up as the handler for the messages. |
| |
| ### Flutter calling into Windows Steps |
| |
| 1) Add the generated C++ code to your `./windows` directory for compilation, and |
| to your `windows/CMakeLists.txt` file. |
| 1) Implement the generated C++ abstract class for handling the calls on Windows, |
| set it up as the handler for the messages. |
| |
| ### Flutter calling into macOS steps |
| |
| 1) Add the generated Objective-C or Swift code to your Xcode project for compilation |
| (e.g. `macos/Runner.xcworkspace` or `.podspec`). |
| 1) Implement the generated protocol for handling the calls on macOS, set it up |
| as the handler for the messages. |
| |
| ### Calling into Flutter from the host platform |
| |
| 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. |
| |
| ### Rules for defining your communication interface |
| |
| 1) The file should contain no method or function definitions, only declarations. |
| 1) Custom classes used by APIs are defined as classes with fields of the |
| supported datatypes (see the supported Datatypes section). |
| 1) APIs should be defined as an `abstract class` with either `HostApi()` or |
| `FlutterApi()` as metadata. The former being for procedures that are defined |
| on the host platform and the latter for procedures that are defined in Dart. |
| 1) 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`. |
| 1) Generics are supported, but can currently only be used with nullable types |
| (example: `List<int?>`). |
| |
| ## Supported Datatypes |
| |
| Pigeon uses the `StandardMessageCodec` so it supports any datatype Platform |
| Channels supports |
| [[documentation](https://flutter.dev/docs/development/platform-integration/platform-channels#codec)]. |
| Nested datatypes are supported, too. |
| |
| ## Features |
| |
| ### Asynchronous Handlers |
| |
| 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: |
| |
| ```dart |
| class Value { |
| int? number; |
| } |
| |
| @HostApi() |
| abstract class Api2Host { |
| @async |
| Value calculate(Value value); |
| } |
| ``` |
| |
| Generates: |
| |
| ```objc |
| // Objective-C |
| @protocol Api2Host |
| -(void)calculate:(nullable Value *)input |
| completion:(void(^)(Value *_Nullable, FlutterError *_Nullable))completion; |
| @end |
| ``` |
| |
| ```swift |
| // Swift |
| |
| /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ |
| protocol Api2Host { |
| func calculate(value: Value, completion: @escaping (Value) -> Void) |
| } |
| ``` |
| |
| ```java |
| // 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 |
| // Kotlin |
| |
| /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ |
| interface Api2Host { |
| fun calculate(value: Value, callback: (Result<Value>) -> Unit) |
| } |
| ``` |
| |
| ```c++ |
| // 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; |
| } |
| ``` |
| |
| ### Null Safety (NNBD) |
| |
| Pigeon supports generating null-safe code, but it doesn't yet support: |
| |
| 1) Nullable generics type arguments |
| 1) Nullable enum arguments to methods |
| |
| ### Enums |
| |
| Pigeon supports enum generation in class fields. For example: |
| ```dart |
| enum State { |
| pending, |
| success, |
| error, |
| } |
| |
| class StateResult { |
| String? errorMessage; |
| State? state; |
| } |
| |
| @HostApi() |
| abstract class Api { |
| StateResult queryState(); |
| } |
| ``` |
| |
| ### Primitive Data-types |
| |
| 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: |
| |
| ```dart |
| @HostApi() |
| abstract class Api { |
| Map<String?, int?> makeMap(List<String?> keys, List<String?> values); |
| } |
| ``` |
| |
| ### TaskQueues |
| |
| When targeting a Flutter version that supports the |
| [TaskQueue API](https://docs.flutter.dev/development/platform-integration/platform-channels?tab=type-mappings-kotlin-tab#channels-and-platform-threading) |
| the threading model for handling HostApi methods can be selected with the |
| `TaskQueue` annotation: |
| |
| ```dart |
| @HostApi() |
| abstract class Api2Host { |
| @TaskQueue(type: TaskQueueType.serialBackgroundThread) |
| int add(int x, int y); |
| } |
| ``` |
| |
| ### Error Handling |
| |
| #### Kotlin, Java and Swift |
| |
| 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 |
| // 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')) |
| } |
| } |
| ``` |
| |
| #### Objective-C and C++ |
| |
| 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 a `FlutterError` reference. |
| * C++ - Return a `FlutterError` directly (for void methods) or within an `ErrorOr` instance. |
| |
| For async methods: |
| * Both - Return a `FlutterError` through the provided callback. |
| |
| #### Handling the errors |
| |
| Then you can implement error handling on the Flutter side: |
| |
| ```dart |
| // 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') |
| } |
| } |
| } |
| ``` |
| |
| ## Feedback |
| |
| File an issue in [flutter/flutter](https://github.com/flutter/flutter) with the |
| word "pigeon" in the title. |