diff --git a/firebase.json b/firebase.json index d8abe03f632..ee74c4e3cc0 100644 --- a/firebase.json +++ b/firebase.json @@ -167,20 +167,23 @@ { "source": "/learn/learning-resources", "destination": "/reference/learning-resources", "type": 301 }, { "source": "/material-3-migration", "destination": "/release/breaking-changes/material-3-migration", "type": 301 }, { "source": "/packages-and-plugins/androidx-compatibility", "destination": "/platform-integration/android/androidx-migration", "type": 301 }, - { "source": "/packages-and-plugins/c-interop", "destination": "/platform-integration/android/c-interop", "type": 301 }, + { "source": "/packages-and-plugins/c-interop", "destination": "/platform-integration/legacy-ffi-plugin", "type": 301 }, { "source": "/packages-and-plugins/plugins-in-tests", "destination": "/testing/plugins-in-tests", "type": 301 }, { "source": "/packages-and-plugins/plugin-api-migration", "destination": "/release/breaking-changes/plugin-api-migration", "type": 301 }, { "source": "/platform-integration/android/androidx-migration", "destination": "/release/breaking-changes/androidx-migration", "type": 301 }, + { "source": "/platform-integration/android/c-interop", "destination": "/platform-integration/legacy-ffi-plugin", "type": 301 }, { "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/c-interop", "destination": "/platform-integration", "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 }, { "source": "/platform-integration/ios/install-ios", "destination": "/platform-integration/ios/setup", "type": 301 }, { "source": "/platform-integration/ios/install-ios/:rest*", "destination": "/platform-integration/ios/setup", "type": 301 }, { "source": "/platform-integration/ios/splash-screen", "destination": "/platform-integration/ios/launch-screen", "type": 301 }, { "source": "/platform-integration/linux/install-linux", "destination": "/platform-integration/linux/setup", "type": 301 }, { "source": "/platform-integration/linux/install-linux/:rest*", "destination": "/platform-integration/linux/setup", "type": 301 }, + { "source": "/platform-integration/macos/c-interop", "destination": "/platform-integration/legacy-ffi-plugin", "type": 301 }, { "source": "/platform-integration/macos/install-macos", "destination": "/platform-integration/macos/setup", "type": 301 }, { "source": "/platform-integration/macos/install-macos/:rest*", "destination": "/platform-integration/macos/setup", "type": 301 }, { "source": "/platform-integration/platform-adaptations", "destination": "/ui/adaptive-responsive/platform-adaptations", "type": 301 }, @@ -809,7 +812,7 @@ { "source": "/to/add-to-app", "destination": "/add-to-app", "type": 301 }, { "source": "/to/add-swift-package-manager-manually", "destination": "/packages-and-plugins/swift-package-manager/for-app-developers#add-to-a-flutter-app-manually", "type": 301 }, { "source": "/to/add-web-support", "destination": "/platform-integration/web/building#add-web-support-to-an-existing-app", "type": 301 }, - { "source": "/to/android-ffi", "destination": "/platform-integration/android/c-interop", "type": 301 }, + { "source": "/to/android-ffi", "destination": "/platform-integration/legacy-ffi-plugin", "type": 301 }, { "source": "/to/android-setup", "destination": "/platform-integration/android/setup", "type": 301 }, { "source": "/to/android-supported-architectures", "destination": "/deployment/android#what-are-the-supported-target-architectures", "type": 301 }, { "source": "/to/asset-from-package", "destination": "/ui/assets/assets-and-images#from-packages", "type": 301 }, @@ -860,8 +863,7 @@ { "source": "/to/ios-create-flutter-engine", "destination": "/add-to-app/ios/add-flutter-screen#create-a-flutterengine", "type": 301 }, { "source": "/to/ios-deploy", "destination": "/deployment/ios", "type": 301 }, { "source": "/to/ios-development-team", "destination": "/deployment/ios#review-xcode-project-settings", "type": 301 }, - { "source": "/to/ios-ffi", "destination": "/platform-integration/ios/c-interop", "type": 301 }, - { "source": "/platform-integration/ios/c-interop", "destination": "/platform-integration/bind-native-code", "type": 301 }, + { "source": "/to/ios-ffi", "destination": "/platform-integration/legacy-ffi-plugin", "type": 301 }, { "source": "/to/ios-minimum-target", "destination": "/deployment/ios#review-xcode-project-settings", "type": 301 }, { "source": "/to/to/java-gradle-incompatibility", "destination": "/release/breaking-changes/android-java-gradle-migration-guide", "type": 301 }, { "source": "/to/java-gradle-incompatibility", "destination": "/release/breaking-changes/android-java-gradle-migration-guide", "type": 301 }, @@ -869,7 +871,7 @@ { "source": "/to/macos-android-setup", "destination": "/platform-integration/android/setup", "type": 301 }, { "source": "/to/macos-deploy", "destination": "/deployment/macos", "type": 301 }, { "source": "/to/macos-entitlements", "destination": "/platform-integration/macos/building#entitlements-and-the-app-sandbox", "type": 301 }, - { "source": "/to/macos-ffi", "destination": "/platform-integration/macos/c-interop", "type": 301 }, + { "source": "/to/macos-ffi", "destination": "/platform-integration/legacy-ffi-plugin", "type": 301 }, { "source": "/to/macos-ios-setup", "destination": "/platform-integration/ios/setup", "type": 301 }, { "source": "/to/macos-minimum-target", "destination": "/deployment/ios#review-xcode-project-settings", "type": 301 }, { "source": "/to/minimize-layout-passes", "destination": "/perf/best-practices#minimize-layout-passes-caused-by-intrinsic-operations", "type": 301 }, diff --git a/sites/docs/src/_includes/docs/resource-links/ffi-video-resources.md b/sites/docs/src/_includes/docs/resource-links/ffi-video-resources.md deleted file mode 100644 index ce5481a0fd9..00000000000 --- a/sites/docs/src/_includes/docs/resource-links/ffi-video-resources.md +++ /dev/null @@ -1,10 +0,0 @@ - -## Other Resources - -To learn more about C interoperability, check out these videos: - -- [C interoperability with Dart FFI] -- [How to Use Dart FFI to Build a Retro Audio Player] - -[C interoperability with Dart FFI]: {{site.yt.watch}}?v=2MMK7YoFgaA -[How to Use Dart FFI to Build a Retro Audio Player]: {{site.yt.watch}}?v=05Wn2oM_nWw 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 4644967a7d7..2c62398b339 100644 --- a/sites/docs/src/content/packages-and-plugins/developing-packages.md +++ b/sites/docs/src/content/packages-and-plugins/developing-packages.md @@ -1026,8 +1026,6 @@ file, like any other Dart package. [Effective Dart Documentation]: {{site.dart-site}}/guides/language/effective-dart/documentation [federated plugins]: #federated-plugins [ffigen docs]: {{site.pub-pkg}}/ffigen/install -[Android]: /platform-integration/android/c-interop -[macOS]: /platform-integration/macos/c-interop [`fluro`]: {{site.pub}}/packages/fluro [Flutter editor]: /tools/editors [Flutter Favorites]: {{site.pub}}/flutter/favorites diff --git a/sites/docs/src/content/platform-integration/android/c-interop.md b/sites/docs/src/content/platform-integration/android/c-interop.md deleted file mode 100644 index b0a6c4cdde4..00000000000 --- a/sites/docs/src/content/platform-integration/android/c-interop.md +++ /dev/null @@ -1,210 +0,0 @@ ---- -title: "Binding to native Android code using dart:ffi" -description: "To use C code in your Flutter program, use the dart:ffi library." ---- - -:::warning -This page documents a legacy approach to C interop on Android. - -Since Flutter 3.38, we recommend using a `package_ffi` template with -[build hooks](/platform-integration/bind-native-code) for C interop. - -However, the legacy FFI plugin template (`plugin_ffi`) documented here is -still useful if you need to: -- Access the Flutter Plugin API. -- Configure a Google Play services runtime on Android. -::: - - - -Flutter mobile and desktop apps can use the -[dart:ffi][] library to call native C APIs. -_FFI_ stands for [_foreign function interface._][FFI] -Other terms for similar functionality include -_native interface_ and _language bindings._ - - -[dart:ffi]: {{site.dart.api}}/dart-ffi/dart-ffi-library.html -[FFI]: https://en.wikipedia.org/wiki/Foreign_function_interface - -Before your library or program can use the FFI library -to bind to native code, you must ensure that the -native code is loaded and its symbols are visible to Dart. -This page focuses on compiling, packaging, -and loading Android native code within a Flutter plugin or app. - -This tutorial demonstrates how to bundle C/C++ -sources in a Flutter plugin and bind to them using -the Dart FFI library on both Android and iOS. -In this walkthrough, you'll create a C function -that implements 32-bit addition and then -exposes it through a Dart plugin named "native_add". - -## Dynamic vs static linking - -A native library can be linked into an app either -dynamically or statically. A statically linked library -is embedded into the app's executable image, -and is loaded when the app starts. - -Symbols from a statically linked library can be -loaded using [`DynamicLibrary.executable`][] or -[`DynamicLibrary.process`][]. - -A dynamically linked library, by contrast, is distributed -in a separate file or folder within the app, -and loaded on-demand. On Android, a dynamically -linked library is distributed as a set of `.so` (ELF) -files, one for each architecture. - -A dynamically linked library can be loaded into -Dart via [`DynamicLibrary.open`][]. - -API documentation is available from the -[Dart API reference documentation][]. - -On Android, only dynamic libraries are supported -(because the main executable is the JVM, -which we don't link to statically). - - -[Dart API reference documentation]: {{site.dart.api}} -[`DynamicLibrary.executable`]: {{site.dart.api}}/dart-ffi/DynamicLibrary/DynamicLibrary.executable.html -[`DynamicLibrary.open`]: {{site.dart.api}}/dart-ffi/DynamicLibrary/DynamicLibrary.open.html -[`DynamicLibrary.process`]: {{site.dart.api}}/dart-ffi/DynamicLibrary/DynamicLibrary.process.html - -## Create an FFI plugin - -To create an FFI plugin called "native_add", -do the following: - -```console -$ flutter create --platforms=android,ios,macos,windows,linux --template=plugin_ffi native_add -$ cd native_add -``` - -:::note -You can exclude platforms from `--platforms` that you don't want -to build to. However, you need to include the platform of -the device you are testing on. -::: - -This will create a plugin with C/C++ sources in `native_add/src`. -These sources are built by the native build files in the various -os build folders. - -The FFI library can only bind against C symbols, -so in C++ these symbols are marked `extern "C"`. - -You should also add attributes to indicate that the -symbols are referenced from Dart, -to prevent the linker from discarding the symbols -during link-time optimization. -`__attribute__((visibility("default"))) __attribute__((used))`. - -On Android, the `native_add/android/build.gradle` links the code. - -The native code is invoked from dart in `lib/native_add_bindings_generated.dart`. - -The bindings are generated with [`package:ffigen`]({{site.pub-pkg}}/ffigen). - -## Other use cases - -### Platform library - -To link against a platform library, -use the following instructions: - - 1. Find the desired library in the [Android NDK Native APIs][] - list in the Android docs. This lists stable native APIs. - 1. Load the library using [`DynamicLibrary.open`][]. - For example, to load OpenGL ES (v3): - - ```dart - DynamicLibrary.open('libGLES_v3.so'); - ``` - -You might need to update the Android manifest -file of the app or plugin if indicated by -the documentation. - - -[Android NDK Native APIs]: {{site.android-dev}}/ndk/guides/stable_apis - -#### First-party library - -The process for including native code in source -code or binary form is the same for an app or -plugin. - -#### Open-source third-party - -Follow the [Add C and C++ code to your project][] -instructions in the Android docs to -add native code and support for the native -code toolchain (either CMake or `ndk-build`). - - -[Add C and C++ code to your project]: {{site.android-dev}}/studio/projects/add-native-code - -#### Closed-source third-party library - -To create a Flutter plugin that includes Dart -source code, but distribute the C/C++ library -in binary form, use the following instructions: - -1. Open the `android/build.gradle` file for your - project. -1. Add the AAR artifact as a dependency. - **Don't** include the artifact in your - Flutter package. Instead, it should be - downloaded from a repository, such as - JCenter. - - -## Android APK size (shared object compression) - -[Android guidelines][] in general recommend -distributing native shared objects uncompressed -because that actually saves on device space. -Shared objects can be directly loaded from the APK -instead of unpacking them on device into a -temporary location and then loading. -APKs are additionally packed in transit—that's -why you should be looking at download size. - -Flutter APKs by default don't follow these guidelines -and compress `libflutter.so` and `libapp.so`—this -leads to smaller APK size but larger on device size. - -Shared objects from third parties can change this default -setting with `android:extractNativeLibs="true"` in their -`AndroidManifest.xml` and stop the compression of `libflutter.so`, -`libapp.so`, and any user-added shared objects. -To re-enable compression, override the setting in -`your_app_name/android/app/src/main/AndroidManifest.xml` -in the following way. - -```xml diff - -+ xmlns:tools="http://schemas.android.com/tools" -+ package="com.example.your_app_name" > - - - -+ android:icon="@mipmap/ic_launcher" -+ android:extractNativeLibs="true" -+ tools:replace="android:extractNativeLibs"> -``` - -[Android guidelines]: {{site.android-dev}}/topic/performance/reduce-apk-size#extract-false - -{% render "docs/resource-links/ffi-video-resources.md", site: site %} diff --git a/sites/docs/src/content/platform-integration/android/setup.md b/sites/docs/src/content/platform-integration/android/setup.md index 0da1bbae3b7..f0e60c1e648 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/bind-native-code.md b/sites/docs/src/content/platform-integration/bind-native-code.md index 3f3fb395326..9ce23b731d2 100644 --- a/sites/docs/src/content/platform-integration/bind-native-code.md +++ b/sites/docs/src/content/platform-integration/bind-native-code.md @@ -17,6 +17,12 @@ If you need to use the Flutter Plugin API, or if you need to configure a Google Play services runtime on Android, use the standard plugin template (`flutter create --template=plugin`). +:::note +Looking for the previous FFI plugin workflow? See +[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 [dart:ffi]: {{site.dart.api}}/dart-ffi/dart-ffi-library.html [FFI]: https://en.wikipedia.org/wiki/Foreign_function_interface @@ -107,6 +113,14 @@ For many system libraries on Android, iOS, Linux, and macOS, you can use For Windows, you often use `DynamicLoadingSystem()` and provide the name of the DLL. +:::note +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 +[Integrating with Windows](/platform-integration/windows/building#integrating-with-windows) +and [Integrate with Linux](/platform-integration/linux/building#integrate-with-linux). +::: + Here is an example `build.dart` that links against system libraries to get the host name: diff --git a/sites/docs/src/content/platform-integration/index.md b/sites/docs/src/content/platform-integration/index.md index 9a209d7370e..712d0384a68 100644 --- a/sites/docs/src/content/platform-integration/index.md +++ b/sites/docs/src/content/platform-integration/index.md @@ -141,12 +141,12 @@ 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
    -

    Learn how to bind to native C code from your app on Android.

    +

    Learn how to bind to native code from your app on Android.

    @@ -188,6 +188,14 @@ 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 +
    +
    +

    Learn how to bind to native code from your app on iOS.

    +
    +
    Embed an iOS view @@ -228,14 +236,6 @@ web platform to your Flutter app.

    Customize how your Flutter app is initialized on the web.

    - -
    - Bind to native code -
    -
    -

    Learn how to bind to native C code from your app on Android.

    -
    -
  • Build and deploy to iOS
  • +
  • + 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 new file mode 100644 index 00000000000..534e6a03369 --- /dev/null +++ b/sites/docs/src/content/platform-integration/legacy-ffi-plugin.md @@ -0,0 +1,403 @@ +--- +title: Bind to native code using the legacy FFI plugin template +description: >- + Use the legacy plugin_ffi template and dart:ffi to bind to + native C code in your Flutter plugin or app. +--- + +:::warning +This page documents the legacy `plugin_ffi` approach to C interop. + +Since Flutter 3.38, we recommend using the `package_ffi` template with +[build hooks][] for C interop. + +However, the legacy FFI plugin template (`plugin_ffi`) documented here is +still useful if you need to: + +- Access the Flutter Plugin API. +- Use static linking (on iOS and macOS). +- Configure a Google Play services runtime on Android. +::: + +Flutter mobile and desktop apps can +use the [`dart:ffi`][] library to call native C APIs. +_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 +[`dart:ffi`]: {{site.dart.api}}/dart-ffi/dart-ffi-library.html +[FFI]: https://en.wikipedia.org/wiki/Foreign_function_interface + +Before your library or program can +use the FFI library to bind to native code, +you must ensure that the native code is +loaded and its symbols are visible to Dart. +This page focuses on compiling, packaging, +and loading native code within a Flutter plugin or app. + +This tutorial demonstrates how to bundle C/C++ sources +in a Flutter plugin and bind to them using the Dart FFI library. +In this walkthrough, you'll create a C function that +implements 32-bit addition and then exposes it through +a Dart plugin named `native_add`. + +## Dynamic versus static linking + +A native library can be linked into an app either +dynamically or statically. A statically linked library +is embedded into the app's executable image, +and is loaded when the app starts. + +Symbols from a statically linked library can be +loaded using [`DynamicLibrary.executable`][] or +[`DynamicLibrary.process`][]. + +A dynamically linked library, by contrast, is distributed +in a separate file or folder within the app, +and loaded on-demand. The distribution format depends on +the platform: + +- On Android, a dynamically linked library is distributed as a + set of `.so` (ELF) files, one for each architecture. + Only dynamic libraries are supported, + because the main executable is the JVM, + which Flutter doesn't link to statically. +- On iOS and macOS, the dynamically linked library is + distributed as a `.framework` folder. + +A dynamically linked library can be loaded into +Dart using [`DynamicLibrary.open`][]. + +[`DynamicLibrary.executable`]: {{site.dart.api}}/dart-ffi/DynamicLibrary/DynamicLibrary.executable.html +[`DynamicLibrary.open`]: {{site.dart.api}}/dart-ffi/DynamicLibrary/DynamicLibrary.open.html +[`DynamicLibrary.process`]: {{site.dart.api}}/dart-ffi/DynamicLibrary/DynamicLibrary.process.html + +## Create an FFI plugin + +To create an FFI plugin called `native_add`, +use `flutter create` with the `plugin_ffi` template: + +```console +$ flutter create --platforms=android,ios,macos,windows,linux --template=plugin_ffi native_add +``` + +:::note +You can exclude platforms from `--platforms` that you don't want to build to. +However, you need to include the platform of the device you are testing on. +::: + +This creates a plugin with C/C++ sources in `native_add/src`. +These sources are built by the native build files in the +various OS build folders. + +The FFI library can only bind against C symbols, +so in C++ these symbols are marked `extern "C"`. + +You should also add attributes to indicate that the +symbols are referenced from Dart, +to prevent the linker from discarding the symbols +during link-time optimization: +`__attribute__((visibility("default"))) __attribute__((used))`. + +The platform-specific build file links the code: + +- On Android, `native_add/android/build.gradle`. +- On iOS, `native_add/ios/native_add.podspec`. +- On macOS, `native_add/macos/native_add.podspec`. +- On Linux, `native_add/linux/CMakeLists.txt`. +- On Windows, `native_add/windows/CMakeLists.txt`. + +The native code is invoked from +Dart in `lib/native_add_bindings_generated.dart`. + +The bindings are generated with [`package:ffigen`][]. + +[`package:ffigen`]: {{site.pub-pkg}}/ffigen + +## Other use cases + +### iOS + +The dynamic linker automatically loads +dynamically linked libraries when the app starts. +Their constituent symbols can be resolved using [`DynamicLibrary.process`][]. +You can also get a handle to the library with [`DynamicLibrary.open`][] to +restrict the scope of symbol resolution, but it's +unclear how Apple's review process handles this. + +Symbols statically linked into the application binary can be +resolved using [`DynamicLibrary.executable`][] or [`DynamicLibrary.process`][]. + +#### Platform library {:#ios-platform-library} + +To link against a platform library, +use the following instructions: + +1. In Xcode, open `Runner.xcworkspace`. +1. Select the target platform. +1. Click **+** in the **Linked Frameworks and Libraries** section. +1. Select the system library to link against. + +#### First-party library {:#ios-first-party-library} + +A first-party native library can be included either +as source or as a (signed) `.framework` file. +It's probably possible to include statically linked +archives as well, but it requires testing. + +#### Source code {:#ios-source-code} + +To link directly to source code, +use the following instructions: + +1. In Xcode, open `Runner.xcworkspace`. +1. Add the C/C++/Objective-C/Swift + source files to the Xcode project. +1. Add the following prefix to the exported symbol declarations to + ensure they are visible to Dart: + + **C/C++/Objective-C:** + + ```objc + extern "C" /* <= C++ only */ __attribute__((visibility("default"))) __attribute__((used)) + ``` + + **Swift:** + + ```swift + @_cdecl("myFunctionName") + ``` + +#### Compiled (dynamic) library {:#ios-compiled-dynamic-library} + +To link to a compiled dynamic library, +use the following instructions: + +1. If a properly signed `Framework` file is present, + open `Runner.xcworkspace`. +1. Add the framework file to the + **Frameworks, Libraries, and Embedded Content** section of + the target in Xcode. +1. Under the **Embed** column, select **Embed & Sign**. + +#### Open-source third-party library {:#ios-open-source-third-party-library} + +To create a Flutter plugin that includes both +C/C++/Objective-C _and_ Dart code, +use the following instructions: + +1. In your plugin project, open `ios/.podspec`. +1. Add the native code to the `source_files` field. + +The native code is then statically linked into +the application binary of any app that uses this plugin. + +#### Closed-source third-party library {:#ios-closed-source-third-party-library} + +To create a Flutter plugin that includes Dart source code, +but distribute the C/C++ library in binary form, +use the following instructions: + +1. In your plugin project, open `ios/.podspec`. +1. Add a `vendored_frameworks` field. + See the [CocoaPods example][]. + +:::warning +**Do not** upload this plugin +(or any plugin containing binary code) to pub.dev. +Instead, this plugin should be downloaded +from a trusted third-party, +as shown in the CocoaPods example. +::: + +[CocoaPods example]: {{site.github}}/CocoaPods/CocoaPods/blob/master/examples/Vendored%20Framework%20Example/Example%20Pods/VendoredFrameworkExample.podspec + +#### Stripping symbols {:#ios-stripping-symbols} + +When creating a release build, Xcode strips the symbols. + +1. In Xcode, select the **Runner** target, + then go to **Build Settings > Strip Style**. +1. Change from **All Symbols** to **Non-Global Symbols**. + +### macOS + +The dynamic linker automatically loads +dynamically linked libraries when the app starts. +Their constituent symbols can be resolved using [`DynamicLibrary.process`][]. +You can also get a handle to the library with [`DynamicLibrary.open`][] to +restrict the scope of symbol resolution, but it's +unclear how Apple's review process handles this. + +Symbols statically linked into the application binary can be +resolved using [`DynamicLibrary.executable`][] or [`DynamicLibrary.process`][]. + +#### Platform library {:#macos-platform-library} + +To link against a platform library, +use the following instructions: + +1. In Xcode, open `Runner.xcworkspace`. +1. Select the target platform. +1. Click **+** in the **Linked Frameworks and Libraries** section. +1. Select the system library to link against. + +#### First-party library {:#macos-first-party-library} + +A first-party native library can be included either +as source or as a (signed) `.framework` file. +It's probably possible to include statically linked +archives as well, but it requires testing. + +#### Source code {:#macos-source-code} + +To link directly to source code, +use the following instructions: + +1. In Xcode, open `Runner.xcworkspace`. +1. Add the C/C++/Objective-C/Swift + source files to the Xcode project. +1. Add the following prefix to the exported symbol declarations to + ensure they are visible to Dart: + + **C/C++/Objective-C:** + + ```objc + extern "C" /* <= C++ only */ __attribute__((visibility("default"))) __attribute__((used)) + ``` + + **Swift:** + + ```swift + @_cdecl("myFunctionName") + ``` + +#### Compiled (dynamic) library {:#macos-compiled-dynamic-library} + +To link to a compiled dynamic library, +use the following instructions: + +1. If a properly signed `Framework` file is present, + open `Runner.xcworkspace`. +1. Add the framework file to the + **Frameworks, Libraries, and Embedded Content** section of + the target in Xcode. +1. Under the **Embed** column, select **Embed & Sign**. + +#### Compiled (dynamic) library, closed source {:#macos-compiled-dynamic-library-closed-source} + +To add a closed source library to a +[Flutter macOS Desktop][] app, +use the following instructions: + +1. Follow the instructions for Flutter desktop to + create a Flutter desktop app. +1. Open the `yourapp/macos/Runner.xcworkspace` in Xcode. + 1. Drag your precompiled library (`libyourlibrary.dylib`) + into `Runner/Frameworks`. + 1. Click `Runner` and go to the `Build Phases` tab. + 1. Drag `libyourlibrary.dylib` into the `Copy Bundle Resources` list. + 1. Under `Embed Libraries`, check `Code Sign on Copy`. + 1. Under `Link Binary With Libraries`, + set status to `Optional`. (We use dynamic linking, + no need to statically link.) + 1. Click `Runner` and go to the `General` tab. + 1. Drag `libyourlibrary.dylib` into the + **Frameworks, Libraries, and Embedded Content** list. + 1. Select **Embed & Sign**. + 1. Click **Runner** and go to the **Build Settings** tab. + 1. In the **Search Paths** section configure the + **Library Search Paths** to include the path + where `libyourlibrary.dylib` is located. +1. Edit `lib/main.dart`. + 1. Use `DynamicLibrary.open('libyourlibrary.dylib')` to + dynamically link to the symbols. + 1. Call your native function somewhere in a widget. +1. Run `flutter run` and check that your native function gets called. +1. Run `flutter build macos` to build a + self-contained release version of your app. + +[Flutter macOS Desktop]: /platform-integration/macos/building + +#### Stripping symbols {:#macos-stripping-symbols} + +When creating a release build, Xcode strips the symbols. + +1. In Xcode, select the **Runner** target, + then go to **Build Settings > Strip Style**. +1. Change from **All Symbols** to **Non-Global Symbols**. + +### Android + +#### Platform library {:#android-platform-library} + +To link against a platform library, +use the following instructions: + +1. Find the desired library in the [Android NDK Native APIs][] + list in the Android docs. This lists stable native APIs. +1. Load the library using [`DynamicLibrary.open`][]. + For example, to load OpenGL ES (v3): + + ```dart + DynamicLibrary.open('libGLES_v3.so'); + ``` + +You might need to update the Android manifest file of the +app or plugin if indicated by the documentation. + +[Android NDK Native APIs]: {{site.android-dev}}/ndk/guides/stable_apis + +#### First-party library {:#android-first-party-library} + +The process for including native code in +source code or binary form is the same for an app or plugin. + +#### Open-source third-party library {:#android-open-source-third-party-library} + +Follow the [Add C and C++ code to your project][] +instructions in the Android docs to +add native code and support for the native +code toolchain (either CMake or `ndk-build`). + +[Add C and C++ code to your project]: {{site.android-dev}}/studio/projects/add-native-code + +#### Closed-source third-party library {:#android-closed-source-third-party-library} + +To create a Flutter plugin that includes Dart source code, +but distribute the C/C++ library in binary form, +use the following instructions: + +1. Open the `android/build.gradle` file for your project. +1. Add the AAR artifact as a dependency. + **Don't** include the artifact in your Flutter package. + Instead, it should be downloaded from a repository, such as Maven Central. + +#### Android APK size (shared object compression) + +[Android guidelines][] in general recommend +distributing native shared objects uncompressed +because that actually saves on device space. +Shared objects can be directly loaded from the APK instead of +unpacking them on the device into a temporary location and then loading. +APKs are additionally packed in transit—that's +why you should be looking at download size. + +By default, Flutter APKs compress `libflutter.so` and `libapp.so`, +which leads to a smaller APK size but a larger on-device size. +To control whether native libraries are stored compressed and extracted at +install time, set the Android Gradle plugin's `useLegacyPackaging` option. +For current recommendations, see the [Android guidelines][]. + +[Android guidelines]: {{site.android-dev}}/topic/performance/reduce-apk-size#extract-false + +## Other resources + +To learn more about C interoperability, check out these videos: + +- [C interoperability with Dart FFI][] +- [How to Use Dart FFI to Build a Retro Audio Player][] + +[C interoperability with Dart FFI]: {{site.yt.watch}}?v=2MMK7YoFgaA +[How to Use Dart FFI to Build a Retro Audio Player]: {{site.yt.watch}}?v=05Wn2oM_nWw diff --git a/sites/docs/src/content/platform-integration/linux/building.md b/sites/docs/src/content/platform-integration/linux/building.md index 79d0e8196a5..8fb102e5ee0 100644 --- a/sites/docs/src/content/platform-integration/linux/building.md +++ b/sites/docs/src/content/platform-integration/linux/building.md @@ -25,6 +25,8 @@ following with native libraries: To learn more about calling C libraries from Flutter, consult [C interop using `dart:ffi`][]. +To bundle and bind your own native C or C++ code with your app, +see [Bind to native code using FFI][]. Many apps benefit from using a package that wraps the underlying library calls in a more convenient, idiomatic Dart API. @@ -38,6 +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 [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/c-interop.md b/sites/docs/src/content/platform-integration/macos/c-interop.md deleted file mode 100644 index 253662f7b05..00000000000 --- a/sites/docs/src/content/platform-integration/macos/c-interop.md +++ /dev/null @@ -1,264 +0,0 @@ ---- -title: "Binding to native macOS code using dart:ffi" -description: "To use C code in your Flutter program, use the dart:ffi library." ---- - -:::warning -This page documents a legacy approach to C interop on macOS. - -Since Flutter 3.38, we recommend using a `package_ffi` template with -[build hooks](/platform-integration/bind-native-code) for C interop. - -However, the legacy FFI plugin template (`plugin_ffi`) documented here is -still useful if you need to: -- Access the Flutter Plugin API. -- Use static linking. -::: - - - -Flutter mobile and desktop apps can use the -[dart:ffi][] library to call native C APIs. -_FFI_ stands for [_foreign function interface._][FFI] -Other terms for similar functionality include -_native interface_ and _language bindings._ - - -[dart:ffi]: {{site.dart.api}}/dart-ffi/dart-ffi-library.html -[FFI]: https://en.wikipedia.org/wiki/Foreign_function_interface - -Before your library or program can use the FFI library -to bind to native code, you must ensure that the -native code is loaded and its symbols are visible to Dart. -This page focuses on compiling, packaging, -and loading macOS native code within a Flutter plugin or app. - -This tutorial demonstrates how to bundle C/C++ -sources in a Flutter plugin and bind to them using -the Dart FFI library on macOS. -In this walkthrough, you'll create a C function -that implements 32-bit addition and then -exposes it through a Dart plugin named "native_add". - -## Dynamic vs static linking - -A native library can be linked into an app either -dynamically or statically. A statically linked library -is embedded into the app's executable image, -and is loaded when the app starts. - -Symbols from a statically linked library can be -loaded using `DynamicLibrary.executable` or -`DynamicLibrary.process`. - -A dynamically linked library, by contrast, is distributed -in a separate file or folder within the app, -and loaded on-demand. On macOS, the dynamically linked -library is distributed as a `.framework` folder. - -A dynamically linked library can be loaded into -Dart using `DynamicLibrary.open`. - -API documentation is available from the -[Dart API reference documentation][]. - - -[Dart API reference documentation]: {{site.dart.api}} - -## Create an FFI plugin - -If you already have a plugin, skip this step. - -To create a plugin called "native_add", -do the following: - -```console -$ flutter create --platforms=macos --template=plugin_ffi native_add -$ cd native_add -``` - -:::note -You can exclude platforms from `--platforms` that you don't want -to build to. However, you need to include the platform of -the device you are testing on. -::: - -This will create a plugin with C/C++ sources in `native_add/src`. -These sources are built by the native build files in the various -os build folders. - -The FFI library can only bind against C symbols, -so in C++ these symbols are marked `extern "C"`. - -You should also add attributes to indicate that the -symbols are referenced from Dart, -to prevent the linker from discarding the symbols -during link-time optimization. -`__attribute__((visibility("default"))) __attribute__((used))`. - -On iOS, the `native_add/macos/native_add.podspec` links the code. - -The native code is invoked from dart in `lib/native_add_bindings_generated.dart`. - -The bindings are generated with [`package:ffigen`]({{site.pub-pkg}}/ffigen). - -## Other use cases - -### iOS and macOS - -Dynamically linked libraries are automatically loaded by -the dynamic linker when the app starts. Their constituent -symbols can be resolved using [`DynamicLibrary.process`][]. -You can also get a handle to the library with -[`DynamicLibrary.open`][] to restrict the scope of -symbol resolution, but it's unclear how Apple's -review process handles this. - -Symbols statically linked into the application binary -can be resolved using [`DynamicLibrary.executable`][] or -[`DynamicLibrary.process`][]. - - -[`DynamicLibrary.executable`]: {{site.dart.api}}/dart-ffi/DynamicLibrary/DynamicLibrary.executable.html -[`DynamicLibrary.open`]: {{site.dart.api}}/dart-ffi/DynamicLibrary/DynamicLibrary.open.html -[`DynamicLibrary.process`]: {{site.dart.api}}/dart-ffi/DynamicLibrary/DynamicLibrary.process.html - -#### Platform library - -To link against a platform library, -use the following instructions: - -1. In Xcode, open `Runner.xcworkspace`. -1. Select the target platform. -1. Click **+** in the **Linked Frameworks and Libraries** - section. -1. Select the system library to link against. - -#### First-party library - -A first-party native library can be included either -as source or as a (signed) `.framework` file. -It's probably possible to include statically linked -archives as well, but it requires testing. - -#### Source code - -To link directly to source code, -use the following instructions: - - 1. In Xcode, open `Runner.xcworkspace`. - 2. Add the C/C++/Objective-C/Swift - source files to the Xcode project. - 3. Add the following prefix to the - exported symbol declarations to ensure they - are visible to Dart: - - **C/C++/Objective-C** - - ```objc - extern "C" /* <= C++ only */ __attribute__((visibility("default"))) __attribute__((used)) - ``` - - **Swift** - - ```swift - @_cdecl("myFunctionName") - ``` - -#### Compiled (dynamic) library - -To link to a compiled dynamic library, -use the following instructions: - -1. If a properly signed `Framework` file is present, - open `Runner.xcworkspace`. -1. Add the framework file to the **Embedded Binaries** - section. -1. Also add it to the **Linked Frameworks & Libraries** - section of the target in Xcode. - -#### Compiled (dynamic) library (macOS) - -To add a closed source library to a -[Flutter macOS Desktop][] app, -use the following instructions: - -1. Follow the instructions for Flutter desktop to create - a Flutter desktop app. -1. Open the `yourapp/macos/Runner.xcworkspace` in Xcode. - 1. Drag your precompiled library (`libyourlibrary.dylib`) - into `Runner/Frameworks`. - 1. Click `Runner` and go to the `Build Phases` tab. - 1. Drag `libyourlibrary.dylib` into the - `Copy Bundle Resources` list. - 1. Under `Embed Libraries`, check `Code Sign on Copy`. - 1. Under `Link Binary With Libraries`, - set status to `Optional`. (We use dynamic linking, - no need to statically link.) - 1. Click `Runner` and go to the `General` tab. - 1. Drag `libyourlibrary.dylib` into the **Frameworks, - Libraries and Embedded Content** list. - 1. Select **Embed & Sign**. - 1. Click **Runner** and go to the **Build Settings** tab. - 1. In the **Search Paths** section configure the - **Library Search Paths** to include the path - where `libyourlibrary.dylib` is located. -1. Edit `lib/main.dart`. - 1. Use `DynamicLibrary.open('libyourlibrary.dylib')` - to dynamically link to the symbols. - 1. Call your native function somewhere in a widget. -1. Run `flutter run` and check that your native function gets called. -1. Run `flutter build macos` to build a self-contained release - version of your app. - -[Flutter macOS Desktop]: /platform-integration/macos/building - -{% comment %} - -#### Open-source third-party library - -To create a Flutter plugin that includes both -C/C++/Objective-C _and_ Dart code, -use the following instructions: - -1. In your plugin project, - open `macos/.podspec`. -1. Add the native code to the `source_files` - field. - -The native code is then statically linked into -the application binary of any app that uses -this plugin. - -#### Closed-source third-party library - -To create a Flutter plugin that includes Dart -source code, but distribute the C/C++ library -in binary form, use the following instructions: - -1. In your plugin project, - open `macos/.podspec`. -1. Add a `vendored_frameworks` field. - See the [CocoaPods example][]. - -:::warning -**Do not** upload this plugin -(or any plugin containing binary code) to pub.dev. -Instead, this plugin should be downloaded -from a trusted third-party, -as shown in the CocoaPods example. -::: - -[CocoaPods example]: {{site.github}}/CocoaPods/CocoaPods/blob/master/examples/Vendored%20Framework%20Example/Example%20Pods/VendoredFrameworkExample.podspec - -## Stripping macOS symbols - -When creating a release archive (IPA), -the symbols are stripped by Xcode. - -1. In Xcode, go to **Target Runner > Build Settings > Strip Style**. -2. Change from **All Symbols** to **Non-Global Symbols**. - -{% endcomment %} - -{% render "docs/resource-links/ffi-video-resources.md", site: site %} diff --git a/sites/docs/src/content/platform-integration/macos/setup.md b/sites/docs/src/content/platform-integration/macos/setup.md index a97ab4c906f..308124bfb3a 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/windows/building.md b/sites/docs/src/content/platform-integration/windows/building.md index 4bc4081c661..6c13855f43d 100644 --- a/sites/docs/src/content/platform-integration/windows/building.md +++ b/sites/docs/src/content/platform-integration/windows/building.md @@ -23,6 +23,8 @@ structs and callbacks, and ABI types like `long` and `size_t`. For more information about calling C libraries from Flutter, see [C interop using `dart:ffi`]. +To bundle and bind your own native C or C++ code with your app, +see [Bind to native code using FFI][]. In practice, while it is relatively straightforward to call basic Win32 APIs from Dart in this way, @@ -45,6 +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 [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/content/release/archive-whats-new.md b/sites/docs/src/content/release/archive-whats-new.md index f8a82e23df0..8bdc174a99c 100644 --- a/sites/docs/src/content/release/archive-whats-new.md +++ b/sites/docs/src/content/release/archive-whats-new.md @@ -1636,7 +1636,7 @@ Happy Fluttering! [Showcase]: {{site.main-url}}/showcase [`ToggleButtons`]: {{site.api}}/flutter/material/ToggleButtons-class.html [ToggleButtons demo]: {{site.github}}/csells/flutter_toggle_buttons -[using the dart:ffi library]: /platform-integration/android/c-interop +[using the dart:ffi library]: /platform-integration/legacy-ffi-plugin ## 09 Jul 2019: 1.7 release