使用 dart:ffi 调用原生代码
Flutter mobile can use the dart:ffi libraryto call native C APIs. FFI stands forforeign function interface.Other terms for similar functionality includenative interface and language bindings.
Before your library or program can use the FFI libraryto bind to native code, you must ensure that thenative 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 usingthe Dart FFI library on both Android and iOS.In this walkthrough, you’ll create a C functionthat implements 32-bit addition and thenexposes it through a Dart plugin named “native_add”.
备忘 The dart:ffi library is in beta, and breaking API changes might still happen.
Using the feature requires a Flutter 1.10.x dev channel build. To switch to the dev channel and upload the latest dev version, do the following:
$ flutter channel dev
$ flutter upgrade
For more information on Flutter’s channels, see Upgrading Flutter.
Dynamic vs static linking
A native library can be linked into an app eitherdynamically or statically. A statically linked libraryis embedded into the app’s executable image,and is loaded when the app starts.
Symbols from a statically linked library can beloaded using DynamicLibrary.executable
orDynamicLibrary.process
.
A dynamically linked library, by contrast, is distributedin a separate file or folder within the app,and loaded on-demand. On Android, a dynamicallylinked library is distributed as a set of .so
(ELF)files, one for each architecture. On iOS,it’s distributed as a .framework
folder.
A dynamically linked library can be loaded intoDart via DynamicLibrary.open
.
API documentation is available from the Dart dev channel:Dart API reference documentation.
Step 1: Create a plugin
If you already have a plugin, skip this step.
To create a plugin called “native_add”,do the following:
$ flutter create --template=plugin native_add
$ cd native_add
Step 2: Add C/C++ sources
You need to inform both the Android and iOS buildsystems about the native code so the code can be compiledand linked appropriately into the final application.
You add the sources to the ios
folder,because CocoaPods doesn’t allow including sourcesabove the podspec file, but Gradle allows you to pointto the ios
folder. It’s not required to use the samesources for both iOS and Android;you may, of course, add Android-specific sourcesto the android
folder and modify CMakeLists.txt
appropriately.
The FFI library can only bind against C symbols,so in C++ these symbols must be marked extern C
.You should also add attributes to indicate that thesymbols are referenced from Dart,to prevent the linker from discarding the symbolsduring link-time optimization.
For example,to create a C++ file named ios/Classes/native_add.cpp
,use the following instructions. (Note that the templatehas already created this file for you.) Start from theroot directory of your project:
cat > ios/Classes/native_add.cpp << EOF
#include <stdint.h>
extern "C" __attribute__((visibility("default"))) __attribute__((used))
int32_t native_add(int32_t x, int32_t y) {
return x + y;
}
EOF
On iOS, you need to tell xcode to statically link the file:
- In Xcode, open
Runner.xcodeproj
. - Add the C/C++/Objective-C/Swiftsource files to the Xcode project.On Android, you need to create a
CMakeLists.txt
fileto define how the sources should be compiled and pointGradle to it. From the root of your project directory,use the following instructions
cat > android/CMakeLists.txt << EOF
cmake_minimum_required(VERSION 3.4.1) # for example
add_library( native_add
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
../ios/Classes/native_add.cpp )
EOF
Finally, add an externalNativeBuild
section toandroid/build.gradle
. For example:
android {
// ...
externalNativeBuild {
// Encapsulates your CMake build configurations.
cmake {
// Provides a relative path to your CMake build script.
path "CMakeLists.txt"
}
}
// ...
}
Step 3: Load the code using the FFI library
In this example, you can add the following code tolib/native_add.dart
. However the location of theDart binding code is not important.
First, you must create a DynamicLibrary
handle tothe native code. This step varies between iOS and Android:
import 'dart:ffi'; // For FFI
import 'dart:io'; // For Platform.isX
final DynamicLibrary nativeAddLib =
Platform.isAndroid
? DynamicLibrary.open("libnative_add.so")
: DynamicLibrary.process();
Note that on Android the native library is namedin CMakeLists.txt
(see above),but on iOS it takes the plugin’s name.
With a handle to the enclosing library,you can resolve the native_add
symbol:
final int Function(int x, int y) nativeAdd =
nativeAddLib
.lookup<NativeFunction<Int32 Function(Int32, Int32)>>("native_add")
.asFunction();
Finally, you can call it. To demonstrate this withinthe auto-generated “example” app (example/lib/main.dart
):
// Inside of _MyAppState.build:
body: Center(
child: Text('1 + 2 == ${nativeAdd(1, 2)}'),
),
Other use cases
iOS and macOS
Dynamically linked libraries are automatically loaded bythe dynamic linker when the app starts. Their constituentsymbols can be resolved using DynamicLibrary.process
.You can also get a handle to the library withDynamicLibrary.open
to restrict the scope ofsymbol resolution, but it’s unclear how Apple’sreview process handles this.
Symbols statically linked into the application binarycan be resolved using DynamicLibrary.executable
orDynamicLibrary.process
.
Platform library
To link against a platform library,use the following instructions:
- In Xcode, open
Runner.xcodeproj
. - Select the target platform.
- Click + in the Linked Frameworks and Librariessection.
- Select the system library to link against.
First-party library
A first-party native library can be included eitheras source or as a (signed) .framework
file.It’s probably possible to include statically linkedarchives as well, but it requires testing.
Source code
To link directly to source code,use the following instructions:
- In Xcode, open
Runner.xcodeproj
. - Add the C/C++/Objective-C/Swiftsource files to the Xcode project.
- Add the following prefix to theexported symbol declarations to ensure theyare visible to Dart:
C/C++/Objective-C
extern "C" /* <= C++ only */ __attribute__((visibility("default"))) __attribute__((used))
Swift
@_cdecl("myFunctionName")
Compiled (dynamic) library
To link to a compiled dynamic library,use the following instructions:
- If a properly signed
Framework
file is present,openRunner.xcodeproj
. - Add the framework file to the Embedded Binariessection.
- Also add it to the Linked Frameworks & Librariessection of the target in Xcode.
Open-source third-party library
To create a Flutter plugin that includes bothC/C++/Objective-C and Dart code,use the following instructions:
- In your plugin project,open
ios/<myproject>.podspec
. - Add the native code to the
source_files
field.The native code is then statically linked intothe application binary of any app that usesthis plugin.
Closed-source third-party library
To create a Flutter plugin that includes Dartsource code, but distribute the C/C++ libraryin binary form, use the following instructions:
- In your plugin project,open
ios/<myproject>.podspec
. - Add a
vendored_frameworks
field.See the CocoaPods example.Do not upload this plugin(or any plugin containing binary code)to Pub. Instead, this plugin should be downloadedfrom a trusted third-party,as shown in the CocoaPods example.
Android
Platform library
To link against a platform library,use the following instructions:
- Find the desired library in the Android NDK Native APIslist in the Android docs. This lists stable native APIs.
- Load the library using
DynamicLibrary.open
.For example, to load OpenGL ES (v3):
DynamicLibrary.open('libGLES_v3.so');
You might need to update the Android manifestfile of the app or plugin if indicated bythe documentation.
First-party library
The process for including native code in sourcecode or binary form is the same for an app orplugin.
Open-source third-party
Follow the Add C and C++ code to your projectinstructions in the Android docs toadd native code and support for the nativecode toolchain (either CMake or ndk-build
).
Closed-source third-party library
To create a Flutter plugin that includes Dartsource code, but distribute the C/C++ libraryin binary form, use the following instructions:
- Open the
android/build.gradle
file for yourproject. - Add the AAR artifact as a dependency.Don’t include the artifact in yourFlutter package. Instead, it should bedownloaded from a repository, such asJCenter.
Web
Plugins are not yet supported for web apps.