diff --git a/sites/docs/firebase.json b/sites/docs/firebase.json index e41438d33c4..e88550f64d7 100644 --- a/sites/docs/firebase.json +++ b/sites/docs/firebase.json @@ -196,6 +196,7 @@ { "source": "/platform-integration/android/install-android", "destination": "/platform-integration/android/setup", "type": 301 }, { "source": "/platform-integration/android/install-android/:rest*", "destination": "/platform-integration/android/setup", "type": 301 }, { "source": "/platform-integration/android/splash-screen-migration", "destination": "/release/breaking-changes/splash-screen-migration", "type": 301 }, + { "source": "/platform-integration/bind-native-code", "destination": "/interop/bind-native-code", "type": 301 }, { "source": "/platform-integration/c-interop", "destination": "/platform-integration/legacy-ffi-plugin", "type": 301 }, { "source": "/platform-integration/ios-app-clip", "destination": "/platform-integration/ios/ios-app-clip", "type": 301 }, { "source": "/platform-integration/ios/c-interop", "destination": "/platform-integration/legacy-ffi-plugin", "type": 301 }, diff --git a/sites/docs/src/content/platform-integration/bind-native-code.md b/sites/docs/src/content/interop/bind-native-code.md similarity index 97% rename from sites/docs/src/content/platform-integration/bind-native-code.md rename to sites/docs/src/content/interop/bind-native-code.md index 9ce23b731d2..5d19bf4c2e4 100644 --- a/sites/docs/src/content/platform-integration/bind-native-code.md +++ b/sites/docs/src/content/interop/bind-native-code.md @@ -18,12 +18,12 @@ Play services runtime on Android, use the standard plugin template (`flutter cre --template=plugin`). :::note -Looking for the previous FFI plugin workflow? See +Looking for the previous FFI plugin workflow? Consult [Bind to native code using the legacy FFI plugin template](/platform-integration/legacy-ffi-plugin), which documents the `plugin_ffi` template and OS-specific build files. ::: -[build hooks]: https://dart.dev/tools/hooks +[build hooks]: {{site.dart-site}}/tools/hooks [dart:ffi]: {{site.dart.api}}/dart-ffi/dart-ffi-library.html [FFI]: https://en.wikipedia.org/wiki/Foreign_function_interface @@ -114,9 +114,9 @@ For Windows, you often use `DynamicLoadingSystem()` and provide the name of the DLL. :::note -For desktop-specific guidance on calling system APIs—including wrapper +For desktop-specific guidance on calling system APIs—including wrapper packages like [`package:win32`]({{site.pub-pkg}}/win32) and -[Canonical's Linux packages]({{site.pub}}/publishers/canonical.com/packages)—see +[Canonical's Linux packages]({{site.pub}}/publishers/canonical.com/packages)—refer to [Integrating with Windows](/platform-integration/windows/building#integrating-with-windows) and [Integrate with Linux](/platform-integration/linux/building#integrate-with-linux). ::: @@ -215,8 +215,8 @@ void main(List args) async { You would need to handle different architectures and platforms by having different versions of your pre-compiled library. -For more examples, see the [code_assets package -examples](https://pub.dev/packages/code_assets/example). +To explore additional examples, consult the +[`package:code_assets` examples]({{site.pub-pkg}}/code_assets/example). ## Dynamic library naming guidelines diff --git a/sites/docs/src/content/interop/dart-ffi.md b/sites/docs/src/content/interop/dart-ffi.md new file mode 100644 index 00000000000..d3fdbda3655 --- /dev/null +++ b/sites/docs/src/content/interop/dart-ffi.md @@ -0,0 +1,62 @@ +--- +title: Interact with native memory using dart:ffi +shortTitle: dart:ffi +description: >- + Learn how to interact with native memory using the dart:ffi library. +--- + +The `dart:ffi` (Foreign Function Interface) library is a core Dart SDK library +that provides the low-level APIs to call C-ABI functions from Dart. + +Rather than code generation tools like [Pigeon](/interop/pigeon), +[FFIgen](/interop/ffigen), +or [JNIgen](/interop/jnigen), +`dart:ffi` is the underlying technology that powers these tools +and enables native memory interop. + +## When to use `dart:ffi` and `package:ffi` + +Dart memory is automatically garbage collected while native C memory is +manually managed, so use `dart:ffi` and `package:ffi` to bridge this gap. + +The two most common scenarios when `package:ffi` is needed alongside `dart:ffi` +are memory allocation and C string conversion. + +With memory allocation, use `package:ffi` to allocate space on the system heap +for native arguments, and manually free that memory when done +to prevent memory leaks. + +With data conversion, use `package:ffi` to convert Dart strings into C strings +(`Pointer` or `Pointer`) so they can be read by native C code. + +## Core tools: `dart:ffi` and `package:ffi` + +To write C interop code, use two libraries: + +- **`dart:ffi`:** Built into the core Dart SDK and required for doing FFI + interop. It provides native representation types, pointers, structs, and + function lookup signatures. +- **`package:ffi`:** A companion package that contains utilities helpful in + common C interop use cases, such as memory allocation (`calloc` and `malloc`) + and C string conversion helpers. + +## Concurrency in Flutter + +FFI calls are synchronous and run on the thread on which they're called. +If you invoke a heavy or blocking C function on your Flutter app's +main UI thread, your app may freeze or drop frames. +To prevent this, execute blocking FFI calls inside a background Dart isolate +using `Isolate.run`. + +## Resources + +To explore examples of memory allocation, string conversion, +and dynamic library loading, refer to these Dart interop resources: + +- [C interop guide (includes code examples for pointers, memory allocation, and structs)][dart-c-interop] +- [`dart:ffi` API reference][dart-ffi-api] +- [`package:ffi` on pub.dev][package-ffi-pub] + +[dart-c-interop]: {{site.dart-site}}/interop/c-interop +[dart-ffi-api]: {{site.dart.api}}/dart-ffi/dart-ffi-library.html +[package-ffi-pub]: {{site.pub-pkg}}/ffi diff --git a/sites/docs/src/content/interop/ffigen.md b/sites/docs/src/content/interop/ffigen.md new file mode 100644 index 00000000000..7a6a685aa64 --- /dev/null +++ b/sites/docs/src/content/interop/ffigen.md @@ -0,0 +1,155 @@ +--- +title: FFIgen +description: >- + Learn how to use FFIgen to generate type-safe Dart bindings + for C, Objective-C, and Swift APIs. +--- + +## Overview + +FFIgen (the Foreign Function Interface generator) is a code generator tool +that creates type-safe Dart bindings for C libraries and Apple frameworks +(Objective-C and Swift). +This allows calling Apple platform APIs (iOS and macOS) directly +and synchronously from your Dart code. + +:::note +Direct Swift interop is in experimental support. +::: + +## When to use FFIgen + +Use FFIgen when you need to call C libraries, +wrap compiled binaries (like C, C++, Rust, or Go), +or interact directly with Objective-C and Swift frameworks on iOS and macOS. + +:::note +FFIgen parses C and Objective-C headers. +To wrap C++ libraries, expose a C-compatible interface +around your C++ code. +FFIgen can't parse C++ classes or templates directly. +::: + +For standard, cross-platform plugin development where you don't have +critical data transfers, use [Pigeon](/interop/pigeon) +instead. + +## Developer flow + +Generate bindings with FFIgen using the following steps: + +1. Reference the native headers. +1. Write the FFIgen configuration script. +1. Generate the bindings by running FFIgen. +1. Call the generated code from Dart. + +To start, identify the native header files or APIs you want to wrap. +Then write a configuration script (typically `tool/ffigen.dart`). +This configuration specifies where the header files are located +and where to output the generated Dart code. + +Next, run the configuration script (`dart run tool/ffigen.dart`), +which parses the headers and generates the corresponding Dart bindings. + +Last, import the generated Dart file into your app and call the functions +directly. +FFIgen communicates directly through the underlying FFI layer, +so refer to the [`dart:ffi` guide](/interop/dart-ffi) +to learn how to manage native memory lifetimes and execute native calls +without blocking the UI thread. + +:::note +If the native API changes, update your configuration script +and regenerate the bindings. +::: + +## Example + +To explore a complete implementation of this flow, +including how to configure the generator, +refer to the [official FFIgen examples][ffigen-examples]. + +The `add` example highlights the general flow with a toy native API +(one function in a single header file). + +The `code_assets` examples highlight real-world examples like +system hostname lookups and audio playback. + +For a production package on pub.dev that uses FFIgen to wrap Apple's +`NSURLSession` API, check out [`package:cupertino_http`][cupertino-http-pub] +([source on GitHub][cupertino-http-src]). + +## Constraints and limitations + +Some general considerations when working with FFIgen: + +### Source header files + +FFIgen uses `libclang` to parse headers. +Always resolve compiler warnings logged during generation +to prevent runtime crashes. + +### OS differences + +Dart has no equivalent of C macros (like `#if TARGET_OS_IPHONE`), +so handle minor API variations using runtime `Platform.isIOS` checks. + +Some Objective-C and Swift-specific pitfalls when working with FFIgen: + +### Objective-C bindings + +Objective-C methods are dynamically resolved. +FFIgen automatically maps method selectors to Dart signatures, +but verify selectors by reading the bindings +if you're using heavily overloaded native methods. + +### Objective-C memory management + +Dart uses garbage collection while Objective-C uses reference counting. +Passing Dart closures that close over native wrapper objects can easily create +reference cycles and leak memory. +To break these reference cycles, wrap the captured object in a `WeakReference`, +as shown in the [Objective-C memory management guide][objc-gc-doc]. + +### Objective-C runtime checks + +FFIgen wrappers enforce Dart static types, +but the underlying Objective-C object might have a different runtime subclass, +so Dart's `is` and `as` keywords won't work as expected. +Use `.isA()` and `.as()` instead, as shown in the +[Objective-C runtime types guide][objc-runtime-types-doc]. + +### Objective-C multithreading + +UI-related Objective-C calls must run on the iOS main platform thread. +Also, some types of callbacks can crash your app +if they're executed on the wrong thread. +To learn more, consult the [Objective-C threading guide][objc-threading-doc]. + +### Swift compatibility + +FFIgen interacts with Swift through Objective-C. +Pure Swift features (such as structs, generics, or enums associated with values) +aren't supported, and classes must be annotated with `@objc`. +For pure Swift interop, explore the Dart team's experimental +[`package:swiftgen`][swiftgen-pub] package, +or use [Pigeon](/interop/pigeon) for production Swift plugins. + +## Resources + +To learn about generating C, Objective-C, and Swift bindings with FFIgen, +refer to the following resources: + +- [FFIgen package on pub.dev][ffigen-pub] +- [`objective_c` package on pub.dev][objc-pub] +- [Official FFIgen examples on GitHub][ffigen-examples] + +[ffigen-pub]: {{site.pub-pkg}}/ffigen +[objc-pub]: {{site.pub-pkg}}/objective_c +[swiftgen-pub]: {{site.pub-pkg}}/swiftgen +[cupertino-http-pub]: {{site.pub-pkg}}/cupertino_http +[cupertino-http-src]: https://github.com/dart-lang/http/tree/master/pkgs/cupertino_http +[ffigen-examples]: https://github.com/dart-lang/native/tree/main/pkgs/ffigen/example +[objc-gc-doc]: https://github.com/dart-lang/native/blob/main/pkgs/ffigen/doc/objc_gc.md +[objc-runtime-types-doc]: https://github.com/dart-lang/native/blob/main/pkgs/ffigen/doc/objc_runtime_types.md +[objc-threading-doc]: https://github.com/dart-lang/native/blob/main/pkgs/ffigen/doc/objc_threading.md diff --git a/sites/docs/src/content/interop/index.md b/sites/docs/src/content/interop/index.md new file mode 100644 index 00000000000..ff3d3990b51 --- /dev/null +++ b/sites/docs/src/content/interop/index.md @@ -0,0 +1,185 @@ +--- +title: Native interop +description: Learn about the options to call platform-specific code in your app. +--- + +Flutter apps run on multiple platforms, +but sometimes you need to communicate directly +with the host operating system or run code from an existing native library. + +To solve this, Flutter provides several interop tools ranging from +code generators to direct FFI bindings. + +Use the following decision tree to choose your interop solution, +or read on to learn about the native interop layers. + +## Choose your interop solution + +```mermaid +flowchart TD + Start(["Question 1: What is your primary goal?"]) --> GoalA["Goal A: Platform integration
(Swift, Kotlin, Java, Obj-C)"] + Start --> GoalB["Goal B: C-ABI wrapping
(C, C++, Rust, Go)"] + Start --> GoalC["Goal C: Pure Dart package
(No Flutter framework)"] + + GoalB --> FFIgen["Use FFIgen
(or Flutter Rust Bridge)"]:::directNode + GoalC --> Direct["Use FFIgen or JNIgen"]:::directNode + + GoalA --> EvalPigeon{"Question 2: Do high-complexity
constraints apply?"} + EvalPigeon -->|"No (most common)"| Pigeon["Use Pigeon
(Recommended default)"]:::recommendedNode + EvalPigeon -->|"Yes (advanced)"| DirectInterop["Use FFIgen or JNIgen"]:::directNode + + classDef recommendedNode stroke:#1e8e3e,stroke-width:2px; + classDef directNode stroke:#1a73e8,stroke-width:2px; +``` + +--- + +Answer the following questions to walk through the decision tree in detail: + +### Question 1: What is your primary goal? + +**Goal A: Platform integration** +: Communicate with native OS features (Swift, Kotlin, Java, or Objective-C) + or expose an API surface. + Proceed to [Question 2: Do high-complexity constraints apply?](#question-2-do-high-complexity-constraints-apply). + +**Goal B: C-ABI wrapping** +: Wrap an existing native library (C, C++, Rust, or Go). + Use [FFIgen](/interop/ffigen) + (or [Flutter Rust Bridge][frb] for Rust). + +**Goal C: Pure Dart package** +: Build a Dart CLI tool without Flutter framework dependencies. + Use [FFIgen](/interop/ffigen) or + [JNIgen](/interop/jnigen). + +### Question 2: Do high-complexity constraints apply? + +If you're building a Flutter plugin, +first verify whether [Pigeon](/interop/pigeon) satisfies +your technical constraints. + +:::important +Migrating a plugin between Pigeon and FFIgen or JNIgen is non-trivial +because the glue code lives in different languages. +Evaluate your plugin's constraints carefully before choosing between them. +::: + +Do any of the following requirements apply to your feature? + +- Your app processes high-frequency raw data (for example, video processing). +- Your integration requires advanced language bridging + (for example, cross-language subclasses, delegates, or proxies). +- Your feature requires manual memory management and multithreading over + guardrails. +- Your target language is C, C++, Rust, or Go. + +Based on whether any of the preceding requirements apply, +choose your interop tool: + +**No (most common) — Use [Pigeon](/interop/pigeon)** +: This is the default, recommended path. + It provides necessary guardrails, creates easily digestible interface + mappings, and is highly efficient for most API surface areas. + +**Yes — Use [FFIgen](/interop/ffigen) or [JNIgen](/interop/jnigen)** +: You need the flexibility, complexity, and performance of direct interop for + larger surface areas. + +:::note +Directly using FFIgen and JNIgen introduces potential pitfalls, +particularly around threading and memory management. +Pigeon is generally recommended because it has fewer potential pitfalls +and provides safer guardrails. +::: + +## Package your interop code + +Depending on how you plan to share your code, +choose the option that best suits your use case. +To learn about the difference between a [package](/resources/glossary#package) +and a [plugin](/resources/glossary#plugin), +refer to the [Flutter glossary](/resources/glossary). + +- **In-app integration.** + Write your native interop code directly inside your Flutter app without + creating a separate package. +- **Plugin.** + A specialized package that makes native platform functionality (such as a + device's camera or sensors) available to your app + (`flutter create --template=plugin`). + For more details, refer to + [Developing plugin packages](/packages-and-plugins/developing-packages#plugin). +- **FFI package.** + A package that compiles and bundles native C or C++ code (or pre-compiled + binaries) using [Dart build hooks](/interop/bind-native-code) and + [`dart:ffi`](/interop/dart-ffi). + This template also includes an FFIgen configuration to regenerate + Dart bindings (`flutter create --template=package_ffi`). + For more details, refer to + [Developing FFI packages](/packages-and-plugins/developing-packages#ffi). + +For more details on package authoring, +refer to [Developing packages and plugins](/packages-and-plugins/developing-packages). + +## Tool comparison + +The following table compares two interop solutions: + +- **Direct interop:** Lower-level direct bindings with higher performance for + large data; requires manual memory and threading management. +- **Pigeon:** Structured, type-safe code generator; recommended default for + Flutter plugins. + +| Target language | Direct interop | Pigeon | +| --- | --- | --- | +| **C** | [FFIgen](/interop/ffigen) | Supported on Linux (GObject) with platform channels | +| **C++ with `extern "C"`** | [FFIgen](/interop/ffigen) | Supported on Windows with platform channels | +| **C++ without `extern "C"`** | FFIgen (not yet supported) | Supported on Windows with platform channels | +| **Rust with `extern "C"`** | [FFIgen](/interop/ffigen) | Not supported directly | +| **Rust without `extern "C"`** | [Flutter Rust Bridge][frb] | Not supported | +| **Go with `extern "C"`** | [FFIgen](/interop/ffigen) | Not supported directly | +| **Go without `extern "C"`** | Not supported | Not supported | +| **Java** | [JNIgen](/interop/jnigen) | Supported through Kotlin (native interop) or directly with platform channels | +| **Kotlin** | [JNIgen](/interop/jnigen) | Supported (recommended) | +| **Objective-C** | [FFIgen](/interop/ffigen) | Supported through Swift (native interop) or directly with platform channels | +| **Swift without Obj-C compatibility annotations** | [`package:swiftgen`][swiftgen-pub] (experimental) | Supported (recommended) | + +{:.table .table-striped} + +## Understand interop layers + +Dart structures native interop into three distinct layers: +low-level bindings, idiomatic Dart APIs, and framework-specific plugins. + +For complete architectural details, +consult the [native interop layers guide][interop-layers]. + +### Layer 1: Low-level bindings + +Layer 1 consists of thin, high-performance, and unopinionated wrappers around +native code (for example, [`dart:ffi`](/interop/dart-ffi), +`dart:js_interop`, [FFIgen](/interop/ffigen), +and [JNIgen](/interop/jnigen)). +These APIs mirror native signatures closely, +but require manual memory and threading management. + +### Layer 2: Idiomatic Dart APIs + +Layer 2 consists of idiomatic Dart APIs that wrap the low-level layer to hide +platform-specific complexities (for example, +[Pigeon](/interop/pigeon) or the +[`sqlite3` package][sqlite3-pub]). +These manage concurrency, memory lifetimes, and data conversions automatically. + +### Layer 3: Framework-specific plugins + +Layer 3 provides integration with framework-specific APIs (for example, the +Flutter Plugin API). +This layer exposes Dart APIs to the framework while hiding platform details +like Android's `Context` or iOS's application lifecycle. + +[frb]: {{site.pub-pkg}}/flutter_rust_bridge +[swiftgen-pub]: {{site.pub-pkg}}/swiftgen +[sqlite3-pub]: {{site.pub-pkg}}/sqlite3 +[interop-layers]: https://github.com/dart-lang/native/blob/main/doc/interop-layers.md diff --git a/sites/docs/src/content/interop/jnigen.md b/sites/docs/src/content/interop/jnigen.md new file mode 100644 index 00000000000..01d10496bd9 --- /dev/null +++ b/sites/docs/src/content/interop/jnigen.md @@ -0,0 +1,139 @@ +--- +title: JNIgen +description: >- + Learn how to use JNIgen to generate type-safe Dart bindings + for Java and Kotlin APIs. +--- + +## Overview + +JNIgen (Java Native Interface generator) is a code generator tool that creates +type-safe Dart bindings for Java and Kotlin libraries and APIs. +This allows calling Android APIs directly and synchronously from your +Dart code. + +## When to use JNIgen + +Use JNIgen when you need to call Java or Kotlin libraries, wrap compiled JAR +or AAR files, or interact directly with Android from your Dart code. + +JNIgen uses direct JNI bindings through `dart:ffi`, +making it the recommended tool when you require high-performance, +low-latency interop, or need to pass massive amounts of raw data between Dart +and the JVM. + +For standard, cross-platform plugin development where you don't have +critical data transfers, use [Pigeon](/interop/pigeon) +instead. + +## Developer flow + +Generate bindings with JNIgen using the following general flow: + +1. Reference the native classes or JARs. +1. Write the JNIgen configuration script. +1. Generate the bindings by running JNIgen. +1. Call the generated code from Dart. + +:::note +For a complete list of configuration options, consult the +[JNIgen documentation on pub.dev][jnigen-pub] and the JNIgen README. +::: + +To start, identify the native Java classes, packages, or compiled JAR/AAR files +you want to wrap. +Then write a configuration script (typically `tool/jnigen.dart`). +This configuration specifies where the native files are located, +what packages to include, and where to output the generated Dart code. + +Next, run the configuration script (`dart run tool/jnigen.dart`), +which parses the classes and generates the corresponding Dart bindings. + +Last, import the generated Dart file into your app and call the functions +directly. +JNIgen communicates directly through the underlying FFI layer, +so refer to the [`dart:ffi` guide](/interop/dart-ffi) +to learn how to manage native memory lifetimes and execute native calls +without blocking the UI thread. + +:::note +If the native API changes, update your configuration script and +regenerate the bindings. +::: + +## Examples + +To explore complete implementations of this flow, +including how to configure the generator, +refer to the [`permissions_plugin` demo][permissions-demo] and the +[`android_toast_demo`][toast-demo] in the official Flutter native interop +demos repository. + +For a production package on pub.dev that uses JNIgen to wrap the Android +Cronet HTTP library, check out [`package:cronet_http`][cronet-http-pub] +([source on GitHub][cronet-http-src]). + +## Constraints and limitations + +Keep these general considerations and limitations in mind when working with +JNIgen: + +### Threading considerations + +JNIgen calls run synchronously on the calling thread. +Watch out for two common scenarios: + +- When calling platform APIs that must run on the platform thread, + use [`runOnPlatformThread`][run-on-platform-thread] from `dart:ui`. +- Dart callbacks from Java run on the isolate that created them. + This can lead to deadlocks if a blocked Java thread waits for a blocked + Dart thread. + +To learn more, consult the [JNIgen threading guide][jnigen-threading-doc]. + +### Lifecycle of Java objects + +JNI wrappers use Java global references. +There is a hard limit of 50,000 active references. +Call `.release()` or use `.using()` blocks to prevent memory leaks. +Use `WeakReference` to break reference cycles between Dart and Java GCs +when implementing interfaces, as shown in the +[JNI object lifecycle guide][jnigen-lifecycle-doc]. + +### JVM runtime type checks + +JNI wrappers are Dart extension types that compile down to `JObject` +at runtime, so Dart's native `is` and `as` keywords don't check the Java +runtime type. +Use `x.isA(Foo.type)` and `x.as(Foo.type)` instead, as shown in the +[Java runtime types guide][jnigen-runtime-types-doc]. + +### General debugging + +Tracking down JNI memory allocation bugs like `DoubleReleaseError` +and `UseAfterReleaseError` can be difficult. +Set `Jni.captureStackTraceOnRelease = true` to log stack traces +and help trace leaks, as explained in the +[JNIgen debugging guide][jnigen-debugging-doc]. + +## Resources + +To learn about generating Java and Kotlin bindings with JNIgen, +refer to the following resources: + +- [JNIgen package on pub.dev][jnigen-pub] +- [JNI package on pub.dev][jni-pub] +- [Official JNIgen examples on GitHub][jnigen-examples] + +[jnigen-pub]: {{site.pub-pkg}}/jnigen +[jni-pub]: {{site.pub-pkg}}/jni +[cronet-http-pub]: {{site.pub-pkg}}/cronet_http +[cronet-http-src]: https://github.com/dart-lang/http/tree/master/pkgs/cronet_http +[jnigen-examples]: https://github.com/dart-lang/native/tree/main/pkgs/jnigen/example +[permissions-demo]: {{site.repo.demos}}/tree/main/native_interop_demos/permissions_plugin +[toast-demo]: {{site.repo.demos}}/tree/main/native_interop_demos/android_toast_demo +[run-on-platform-thread]: {{site.api}}/flutter/dart-ui/runOnPlatformThread.html +[jnigen-threading-doc]: https://github.com/dart-lang/native/blob/main/pkgs/jnigen/doc/threading.md +[jnigen-lifecycle-doc]: https://github.com/dart-lang/native/blob/main/pkgs/jnigen/doc/lifecycle.md +[jnigen-runtime-types-doc]: https://github.com/dart-lang/native/blob/main/pkgs/jnigen/doc/java_runtime_types.md +[jnigen-debugging-doc]: https://github.com/dart-lang/native/blob/main/pkgs/jnigen/doc/debugging.md diff --git a/sites/docs/src/content/interop/pigeon.md b/sites/docs/src/content/interop/pigeon.md new file mode 100644 index 00000000000..cd43b1eac18 --- /dev/null +++ b/sites/docs/src/content/interop/pigeon.md @@ -0,0 +1,138 @@ +--- +title: Pigeon +description: >- + 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. + +:::note +For low-level integrations that require direct access to native memory +or C and C++ libraries, use [FFIgen](/interop/ffigen) +or [JNIgen](/interop/jnigen) instead. +::: + +### Pigeon with native interop + +Pigeon with native interop uses [FFIgen](/interop/ffigen) +and [JNIgen](/interop/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: + +1. Create the Pigeon definition file. +1. Generate the code. +1. Implement the native interface. +1. 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. + +:::note +If you're using Pigeon with the native interop communication option, +set `appDirectory`, `swiftOptions: SwiftOptions(useFfi: true, ffiModuleName: 'Runner')`, +and `kotlinOptions: KotlinOptions(useJni: true)` in the `@ConfigurePigeon` +section of your Pigeon definition file. +For complete prerequisites and build configuration, +refer to the [native interop guide][pigeon-native-guide]. +::: + +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 `.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][pigeon-native-example] +- [Example of Pigeon with platform channels][pigeon-example] +- [`shared_preferences` plugin][shared-preferences-source] + +## 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](/interop/ffigen) or +[JNIgen](/interop/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: + +- [Migrating from Pigeon with platform channels to Pigeon with native interop][pigeon-migration-guide] +- [Pigeon native interop guide][pigeon-native-guide] +- [Pigeon package on pub.dev][pigeon-pub] +- [Pigeon source code][pigeon-source] +- [Flutter native interop demos][native-interop-demos] + +[pigeon-pub]: {{site.pub-pkg}}/pigeon +[pigeon-source]: {{site.repo.packages}}/tree/main/packages/pigeon +[pigeon-example]: {{site.repo.packages}}/tree/main/packages/pigeon/example +[pigeon-native-example]: {{site.repo.packages}}/tree/main/packages/pigeon/example/native_interop_app +[pigeon-native-guide]: {{site.repo.packages}}/blob/main/packages/pigeon/native_interop_guide.md +[pigeon-migration-guide]: {{site.repo.packages}}/blob/main/packages/pigeon/native_interop_migration_guide.md +[shared-preferences-source]: {{site.repo.packages}}/tree/main/packages/shared_preferences +[native-interop-demos]: {{site.repo.demos}}/tree/main/native_interop_demos diff --git a/sites/docs/src/content/packages-and-plugins/developing-packages.md b/sites/docs/src/content/packages-and-plugins/developing-packages.md index 3966759dc7b..4182742c541 100644 --- a/sites/docs/src/content/packages-and-plugins/developing-packages.md +++ b/sites/docs/src/content/packages-and-plugins/developing-packages.md @@ -1065,7 +1065,7 @@ All web dependencies are handled by the `pubspec.yaml` file, like any other Dart package. [Apple's PackageDescription documentation]: {{site.apple-dev}}/documentation/packagedescription -[bind-native]: /platform-integration/bind-native-code +[bind-native]: /interop/bind-native-code [CocoaPods Documentation]: https://guides.cocoapods.org/syntax/podspec.html [Dart library package]: {{site.dart-site}}/guides/libraries/create-library-packages [`device_info_plus`]: {{site.pub-api}}/device_info_plus diff --git a/sites/docs/src/content/platform-integration/android/call-jetpack-apis.md b/sites/docs/src/content/platform-integration/android/call-jetpack-apis.md index d2476b9145d..d1d78c14dce 100644 --- a/sites/docs/src/content/platform-integration/android/call-jetpack-apis.md +++ b/sites/docs/src/content/platform-integration/android/call-jetpack-apis.md @@ -54,21 +54,16 @@ two options. The most direct and efficient way to invoke native APIs is by calling the API directly, via FFI. This links your Dart executable to any specified native code at compile-time, allowing you to -call it directly from the UI thread through a small amount of glue -code. In most cases, [ffigen][ffigen] or [jnigen][jnigen] are -helpful in writing this glue code. +call it directly from the UI thread through generated bindings. +On Android, [JNIgen][jnigen] generates Dart bindings for Java and +Kotlin APIs, and [FFIgen][ffigen] generates bindings for C APIs. -For complete guidance on directly calling native code from -your Flutter app, see the [FFI documentation][ffi]. +For complete guidance on calling native code directly from +your Flutter app, consult the [Native interop documentation][interop]. -In the coming months, the Dart team hopes to make this process -easier with direct support for calling native APIs using the -FFI approach, but without any need for the developer to write -glue code. - -[ffi]: {{site.dart-site}}/interop/c-interop -[ffigen]: {{site.pub}}/packages/ffigen -[jnigen]: {{site.pub}}/packages/jnigen +[interop]: /interop +[ffigen]: /interop/ffigen +[jnigen]: /interop/jnigen ### Add a MethodChannel @@ -81,11 +76,11 @@ might or might not matter to you, depending on your use case. As with FFI and direct calls to native code, using a `MethodChannel` requires a small amount of glue code to translate your Dart objects into native objects, and then back again. In most cases, -[`pkg:pigeon`][pigeon] is helpful in writing this glue code. +[Pigeon][pigeon] is helpful in generating this glue code. For complete guidance on adding MethodChannels to your Flutter -app, see the [`MethodChannel`s documentation][methodchannels]. +app, consult the [`MethodChannel`s documentation][methodchannels]. [methodchannels]: /platform-integration/platform-channels [methodchannels-api-docs]: {{site.api}}/flutter/services/MethodChannel-class.html -[pigeon]: {{site.pub}}/packages/pigeon +[pigeon]: /interop/pigeon diff --git a/sites/docs/src/content/platform-integration/android/setup.md b/sites/docs/src/content/platform-integration/android/setup.md index f0e60c1e648..a1e79c7b637 100644 --- a/sites/docs/src/content/platform-integration/android/setup.md +++ b/sites/docs/src/content/platform-integration/android/setup.md @@ -377,7 +377,7 @@ or begin improving integration with Android. Build and deploy to Android
  • - Bind to native Android code + Bind to native Android code
  • Add a splash screen diff --git a/sites/docs/src/content/platform-integration/index.md b/sites/docs/src/content/platform-integration/index.md index b2818eeb42e..442b5b56f33 100644 --- a/sites/docs/src/content/platform-integration/index.md +++ b/sites/docs/src/content/platform-integration/index.md @@ -22,8 +22,8 @@ For example, you might want to use a native library that's only available on iOS and iPadOS. For many use cases, you can find and use one of the many [Flutter plugins][] provided by the Flutter team and the amazing Flutter community. -If none of them meet your needs, you can -[write platform-specific code][] and even [create your own plugin][]. +If none of them meet your needs, you can explore [Native interop][], +[write platform-specific code][], or [create your own plugin][]. :::tip If you're exploring building your app for multiple platforms, @@ -32,6 +32,7 @@ also consider building your UI with [adaptive and responsive design][] in mind. [supported platforms]: /reference/supported-platforms [Flutter plugins]: /packages-and-plugins/using-packages +[Native interop]: /interop [write platform-specific code]: /platform-integration/platform-channels [create your own plugin]: /packages-and-plugins/developing-packages [adaptive and responsive design]: /ui/adaptive-responsive/ @@ -141,7 +142,7 @@ Learn how to add custom integrations with Android to your Flutter app.

    Learn how the latest Android APIs in your app from Dart.

    - +
    Bind to native code
    @@ -196,7 +197,7 @@ Learn how to add custom integrations with iOS to your Flutter app.

    Learn about plugins that support functionality from native iOS frameworks.

    - +
    Bind to native code
    @@ -309,7 +310,7 @@ Learn how to add custom integrations with Windows to your Flutter app. Learn how to add custom integrations with macOS to your Flutter app.
  • - Bind to native iOS code + Bind to native iOS code
  • Leverage system frameworks diff --git a/sites/docs/src/content/platform-integration/legacy-ffi-plugin.md b/sites/docs/src/content/platform-integration/legacy-ffi-plugin.md index ee0580c7a8f..31855f9b125 100644 --- a/sites/docs/src/content/platform-integration/legacy-ffi-plugin.md +++ b/sites/docs/src/content/platform-integration/legacy-ffi-plugin.md @@ -25,7 +25,7 @@ _FFI_ stands for [_foreign function interface._][FFI] Other terms for similar functionality include _native interface_ and _language bindings._ -[build hooks]: /platform-integration/bind-native-code +[build hooks]: /interop/bind-native-code [`dart:ffi`]: {{site.dart.api}}/dart-ffi/dart-ffi-library.html [FFI]: https://en.wikipedia.org/wiki/Foreign_function_interface diff --git a/sites/docs/src/content/platform-integration/linux/building.md b/sites/docs/src/content/platform-integration/linux/building.md index 8fb102e5ee0..7b8ee8188ad 100644 --- a/sites/docs/src/content/platform-integration/linux/building.md +++ b/sites/docs/src/content/platform-integration/linux/building.md @@ -40,7 +40,7 @@ including common packages such as [`url_launcher`], [`shared_preferences`], [`file_selector`], and [`path_provider`]. [C interop using `dart:ffi`]: {{site.dart-site}}/guides/libraries/c-interop -[Bind to native code using FFI]: /platform-integration/bind-native-code +[Bind to native code using FFI]: /interop/bind-native-code [Canonical]: {{site.pub}}/publishers/canonical.com/packages [support-linux]: {{site.pub}}/packages?q=platform%3Alinux [`url_launcher`]: {{site.pub-pkg}}/url_launcher diff --git a/sites/docs/src/content/platform-integration/macos/setup.md b/sites/docs/src/content/platform-integration/macos/setup.md index df2ffe27284..77946c6fe65 100644 --- a/sites/docs/src/content/platform-integration/macos/setup.md +++ b/sites/docs/src/content/platform-integration/macos/setup.md @@ -169,7 +169,7 @@ or begin expanding integration with macOS. Build and deploy to macOS
  • - Bind to native macOS code + Bind to native macOS code
  • Embed native macOS views diff --git a/sites/docs/src/content/platform-integration/platform-channels.md b/sites/docs/src/content/platform-integration/platform-channels.md index c367cf8cf01..9c2bc7d253e 100644 --- a/sites/docs/src/content/platform-integration/platform-channels.md +++ b/sites/docs/src/content/platform-integration/platform-channels.md @@ -1,26 +1,37 @@ --- -title: Writing custom platform-specific code -shortTitle: Platform-specific code -description: Learn how to write custom platform-specific code in your app. +title: Write platform-specific code with platform channels +shortTitle: Platform channels +description: Learn how to write platform-specific code using Flutter's platform channels. --- -This guide describes how to use custom platform-specific -code with Flutter. +:::note +Refer to the [Native interop overview](/interop) +for recommended approaches to calling native platform code. +::: + +Platform channels pass messages between Flutter and your host platform +(Android, iOS, macOS, Windows, or Linux). + +While [Pigeon](/interop/pigeon), +[FFIgen](/interop/ffigen), and +[JNIgen](/interop/jnigen) are recommended for new projects, +platform channels remain available for low-level message passing and +maintaining existing codebases. ## Overview You can use platform-specific code in your Flutter app. -A few common ways to do this include: +Common ways to do this include: -* Use Flutter's platform channel APIs to pass messages - between Flutter and your desired platforms. For more - information, see [Call platform-specific code using platform channels](#example). +- Use Flutter's platform channel APIs to pass messages + between Flutter and your target platforms. + For details, refer to + [Call platform-specific code using platform channels](#example). -* Use the `Pigeon` package to generate type-safe - platform-specific code. For more - information, see [Call platform-specific code using the Pigeon package](#pigeon). +- Use [Pigeon](/interop/pigeon) to generate type-safe + platform-specific bindings. Flutter supports the following platforms and platform-specific languages: @@ -1130,71 +1141,11 @@ it displays 'Battery level not available'. ## Call platform-specific code using the Pigeon package {:#pigeon} -You can use the [`Pigeon`][] package as -an alternative to Flutter's platform channel APIs -to generate code that sends messages in a -structured, type-safe manner. The workflow for Pigeon -looks like this: - - * The Flutter app sends structured - type-safe messages to its _host_, the non-Dart portion - of the app, over a platform channel. - - * The _host_ listens on the platform channel, and receives - the message. It then calls into any number of - platform-specific APIs using the native programming - language and sends a response back to the _client_, - the Flutter portion of the app. - -Using this package eliminates the need to match -strings between host and client for the names and -data types of messages. It supports nested classes, -grouping messages into APIs, generation of asynchronous -wrapper code, and sending messages in either direction. The -generated code is readable and guarantees there are no -conflicts between multiple clients of different versions. - -With Pigeon, the messaging protocol is defined -in a subset of Dart that then generates messaging -code for Android, iOS, macOS, or Windows. For example: - - -```dart title="pigeon_source.dart" -import 'package:pigeon/pigeon.dart'; - -class SearchRequest { - final String query; - - SearchRequest({required this.query}); -} - -class SearchReply { - final String result; - - SearchReply({required this.result}); -} - -@HostApi() -abstract class Api { - @async - SearchReply search(SearchRequest request); -} -``` - - -```dart title="use_pigeon.dart" -import 'generated_pigeon.dart'; - -Future onClick() async { - SearchRequest request = SearchRequest(query: 'test'); - Api api = SomeApi(); - SearchReply reply = await api.search(request); - print('reply: ${reply.result}'); -} -``` - -You can find a complete example and more information -on the [`pigeon`][] page on pub.dev. +Pigeon is the recommended default tool for generating type-safe bindings +between Dart and host platforms. +To learn how to use Pigeon, define API contracts, and choose between +native interop and platform channels, +refer to the dedicated [Pigeon guide](/interop/pigeon). ## Channels and platform threading @@ -1474,7 +1425,6 @@ in the Flutter ecosystem, see [publishing packages][]. [`FirestoreMessageCodec`]: {{site.repo.flutterfire}}/blob/master/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/utils/firestore_message_codec.dart [developing packages]: /packages-and-plugins/developing-packages [Engine Embedder APIs]: {{site.dart.api}}/index.html#more-documentation -[`Pigeon`]: {{site.pub-pkg}}/pigeon [plugins]: /packages-and-plugins/developing-packages#plugin [dispatch queue]: {{site.apple-dev}}/documentation/dispatch/dispatchqueue [`/examples/platform_channel/`]: {{site.repo.flutter}}/tree/main/examples/platform_channel diff --git a/sites/docs/src/content/platform-integration/windows/building.md b/sites/docs/src/content/platform-integration/windows/building.md index 6c13855f43d..e2d53a56f23 100644 --- a/sites/docs/src/content/platform-integration/windows/building.md +++ b/sites/docs/src/content/platform-integration/windows/building.md @@ -47,7 +47,7 @@ More generally, many other [packages support Windows], including common packages such as [`url_launcher`], [`shared_preferences`], [`file_selector`], and [`path_provider`]. [C interop using `dart:ffi`]: {{site.dart-site}}/guides/libraries/c-interop -[Bind to native code using FFI]: /platform-integration/bind-native-code +[Bind to native code using FFI]: /interop/bind-native-code [win32 package]: {{site.pub}}/packages/win32 [Windows registry]: {{site.pub}}/packages/win32_registry [gamepad support]: {{site.pub}}/packages/win32_gamepad diff --git a/sites/docs/src/data/sidenav/default.yml b/sites/docs/src/data/sidenav/default.yml index 8e715f3889d..81499265841 100644 --- a/sites/docs/src/data/sidenav/default.yml +++ b/sites/docs/src/data/sidenav/default.yml @@ -363,6 +363,28 @@ - title: Design patterns permalink: /app-architecture/design-patterns +- title: Native interop + permalink: /interop + icon: alt_route + children: + - title: Overview + permalink: /interop + - title: Pigeon + permalink: /interop/pigeon + - title: FFIgen + permalink: /interop/ffigen + children: + - title: Overview + permalink: /interop/ffigen + - title: "dart:ffi" + permalink: /interop/dart-ffi + - title: Bind to native code + permalink: /interop/bind-native-code + - title: JNIgen + permalink: /interop/jnigen + - title: Platform channels + permalink: /platform-integration/platform-channels + - title: Platform integration permalink: /platform-integration icon: integration_instructions @@ -371,10 +393,6 @@ permalink: /reference/supported-platforms - title: Build desktop apps with Flutter permalink: /platform-integration/desktop - - title: Write platform-specific code - permalink: /platform-integration/platform-channels - - title: Bind to native code - permalink: /platform-integration/bind-native-code - title: Android permalink: /platform-integration/android children: