From 4a42c34944ae6edaf1abc3b6c7fffa14eb7ce79a Mon Sep 17 00:00:00 2001 From: Michael Thomsen Date: Wed, 11 May 2022 20:08:41 +0200 Subject: [PATCH 1/4] FFI plugin docs --- .../developing-packages.md | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/src/development/packages-and-plugins/developing-packages.md b/src/development/packages-and-plugins/developing-packages.md index e492320882..299cf817ab 100644 --- a/src/development/packages-and-plugins/developing-packages.md +++ b/src/development/packages-and-plugins/developing-packages.md @@ -62,6 +62,11 @@ Packages can contain more than one kind of content: see the Medium article by Harry Terkelsen, [How to Write a Flutter Web Plugin, Part 1][]. +**FFI Plugin packages** +: A specialized Dart package that contains an API written in + Dart code combined with one or more platform-specific + implementations that use [Dart FFI][FFI]. + ## Developing Dart packages {#dart} The following instructions explain how to write a Flutter @@ -472,6 +477,95 @@ functionality does not regress as you make changes to your code. For more information, see [Testing your plugin][], a section in [Supporting the new Android plugins APIs][]. +## Developing FFI plugin packages {#plugin-ffi} + +If you want to develop a package that calls into native APIs using +[Dart's FFI][FFI], you need to develop a FFI plugin package. + +### Step 1: Create the package + +To create a starter Flutter package, +use the `--template=plugin_ffi` flag with `flutter create`: + +```terminal +$ flutter create --template=plugin_ffi hello +``` + +This creates a FFI plugin project in the `hello` +folder with the following specialized content:: + +**lib**: The Dart code that defines the API of the plugin, and which +calls into the native code using `dart:ffi`. + +**src**: The native source code, and a `CmakeFile.txt` file for building +that source code into a dynamic library. + +**platform folders** (`android`, `ios`, `windows`, etc.): The build files +for building and bundling the native code library with the platform application. + +### Step 2: Buidling and bundling native code + +The `pubspec.yaml` specifies FFI plugins as follows: + +```yaml + plugin: + platforms: + some_platform: + ffiPlugin: true +``` + +This configuration invokes the native build for the various target platforms +and bundles the binaries in Flutter applications using these FFI plugins. + +This can be combined with `dartPluginClass`, such as when FFI is used for the +implementation of one platform in a federated plugin: + +```yaml + plugin: + implements: some_other_plugin + platforms: + some_platform: + dartPluginClass: SomeClass + ffiPlugin: true +``` + +A plugin can have both FFI and method channels: + +```yaml + plugin: + platforms: + some_platform: + pluginClass: SomeName + ffiPlugin: true +``` + +The native build systems that are invoked by FFI (and method channel) plugins are: + +* For Android: Gradle, which invokes the Android NDK for native builds. + * See the documentation in `android/build.gradle`. +* For iOS and MacOS: Xcode, via CocoaPods. + * See the documentation in `ios/hello.podspec`. + * See the documentation in `macos/hello.podspec`. +* For Linux and Windows: CMake. + * See the documentation in `linux/CMakeLists.txt`. + * See the documentation in `windows/CMakeLists.txt`. + +### Step 3: Binding to native code + +To use the native code, bindings in Dart are needed. +To avoid writing these by hand, they are generated from the header file +(`src/hello.h`) by [`package:ffigen`][]. +Regenerate the bindings by running `flutter pub run ffigen --config ffigen.yaml`. + +### Step 4: Invoking native code + +Very short-running native functions can be directly invoked from any isolate. +For example, see `sum` in `lib/hello.dart`. + +Longer-running functions should be invoked on a helper isolate to avoid +dropping frames in Flutter applications. +For example, see `sumAsync` in `lib/hello.dart`. + ## Adding documentation It is recommended practice to add the following documentation @@ -720,6 +814,7 @@ PENDING [issue #33302]: {{site.repo.flutter}}/issues/33302 [`LICENSE`]: #adding-licenses-to-the-license-file [`path`]: {{site.pub}}/packages/path +[`package:ffigen`]: {{site.pub}}/packages/ffigen [platform channel]: {{site.url}}/development/platform-integration/platform-channels [pub.dev]: {{site.pub}} [publishing docs]: {{site.dart-site}}/tools/pub/publishing From 2f2b92f99879250044b2a6129ca1d81be1bd884d Mon Sep 17 00:00:00 2001 From: Michael Thomsen Date: Wed, 11 May 2022 20:34:30 +0200 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Parker Lougheed --- .../packages-and-plugins/developing-packages.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/development/packages-and-plugins/developing-packages.md b/src/development/packages-and-plugins/developing-packages.md index 299cf817ab..04267bbf3d 100644 --- a/src/development/packages-and-plugins/developing-packages.md +++ b/src/development/packages-and-plugins/developing-packages.md @@ -484,7 +484,7 @@ If you want to develop a package that calls into native APIs using ### Step 1: Create the package -To create a starter Flutter package, +To create a starter FFI plugin package, use the `--template=plugin_ffi` flag with `flutter create`: ```terminal @@ -492,7 +492,7 @@ $ flutter create --template=plugin_ffi hello ``` This creates a FFI plugin project in the `hello` -folder with the following specialized content:: +folder with the following specialized content: **lib**: The Dart code that defines the API of the plugin, and which calls into the native code using `dart:ffi`. @@ -539,7 +539,7 @@ A plugin can have both FFI and method channels: ffiPlugin: true ``` -The native build systems that are invoked by FFI (and method channel) plugins are: +The native build systems that are invoked by FFI (and method channels) plugins are: * For Android: Gradle, which invokes the Android NDK for native builds. * See the documentation in `android/build.gradle`. @@ -560,7 +560,7 @@ Regenerate the bindings by running `flutter pub run ffigen --config ffigen.yaml` ### Step 4: Invoking native code Very short-running native functions can be directly invoked from any isolate. -For example, see `sum` in `lib/hello.dart`. +For an example, see `sum` in `lib/hello.dart`. Longer-running functions should be invoked on a helper isolate to avoid dropping frames in Flutter applications. From d6fddd70c5437467b8ed488db8a17715963a3766 Mon Sep 17 00:00:00 2001 From: Michael Thomsen Date: Wed, 11 May 2022 20:49:41 +0200 Subject: [PATCH 3/4] more review feedback --- .../packages-and-plugins/developing-packages.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/development/packages-and-plugins/developing-packages.md b/src/development/packages-and-plugins/developing-packages.md index 04267bbf3d..d3185a0088 100644 --- a/src/development/packages-and-plugins/developing-packages.md +++ b/src/development/packages-and-plugins/developing-packages.md @@ -553,16 +553,21 @@ The native build systems that are invoked by FFI (and method channels) plugins a ### Step 3: Binding to native code To use the native code, bindings in Dart are needed. + To avoid writing these by hand, they are generated from the header file (`src/hello.h`) by [`package:ffigen`][]. -Regenerate the bindings by running `flutter pub run ffigen --config ffigen.yaml`. +Regenerate the bindings by running: + +```terminal +$ flutter pub run ffigen --config ffigen.yaml +``` ### Step 4: Invoking native code Very short-running native functions can be directly invoked from any isolate. For an example, see `sum` in `lib/hello.dart`. -Longer-running functions should be invoked on a helper isolate to avoid +Longer-running functions should be invoked on a [helper isolate][] to avoid dropping frames in Flutter applications. For example, see `sumAsync` in `lib/hello.dart`. @@ -809,6 +814,7 @@ PENDING [Flutter Favorites]: {{site.pub}}/flutter/favorites [Flutter Favorites program]: {{site.url}}/development/packages-and-plugins/favorites [Gradle Documentation]: https://docs.gradle.org/current/userguide/tutorial_using_tasks.html +[helper isolate]: {{site.dart-site}}/guides/language/concurrency#background-workers [How to Write a Flutter Web Plugin, Part 1]: {{site.flutter-medium}}/how-to-write-a-flutter-web-plugin-5e26c689ea1 [How To Write a Flutter Web Plugin, Part 2]: {{site.flutter-medium}}/how-to-write-a-flutter-web-plugin-part-2-afdddb69ece6 [issue #33302]: {{site.repo.flutter}}/issues/33302 From f00d021fa12a5ffa9527b340a6847e527ceef0f6 Mon Sep 17 00:00:00 2001 From: Michael Thomsen Date: Wed, 11 May 2022 21:01:16 +0200 Subject: [PATCH 4/4] Update src/development/packages-and-plugins/developing-packages.md Co-authored-by: Parker Lougheed --- src/development/packages-and-plugins/developing-packages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/development/packages-and-plugins/developing-packages.md b/src/development/packages-and-plugins/developing-packages.md index d3185a0088..1df0ef728a 100644 --- a/src/development/packages-and-plugins/developing-packages.md +++ b/src/development/packages-and-plugins/developing-packages.md @@ -569,7 +569,7 @@ For an example, see `sum` in `lib/hello.dart`. Longer-running functions should be invoked on a [helper isolate][] to avoid dropping frames in Flutter applications. -For example, see `sumAsync` in `lib/hello.dart`. +For an example, see `sumAsync` in `lib/hello.dart`. ## Adding documentation