Bind to native code using the legacy FFI plugin template
Use the legacy plugin_ffi template and dart:ffi to bind to native C code in your Flutter plugin or app.
Flutter mobile and desktop apps can
use the dart:ffi
library to call native C APIs.
FFI stands for foreign function interface.
Other terms for similar functionality include
native interface and language bindings.
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
.frameworkfolder.
A dynamically linked library can be loaded into
Dart using DynamicLibrary.open.
Create an FFI plugin
#
To create an FFI plugin called native_add,
use flutter create with the plugin_ffi template:
flutter create --platforms=android,ios,macos,windows,linux --template=plugin_ffi native_add
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.
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
#To link against a platform library, use the following instructions:
- In Xcode, open
Runner.xcworkspace. - Select the target platform.
- Click + in the Linked Frameworks and Libraries section.
- 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:
In Xcode, open
Runner.xcworkspace.-
Add the C/C++/Objective-C/Swift source files to the Xcode project.
-
Add the following prefix to the exported symbol declarations to ensure they are visible to Dart:
C/C++/Objective-C:
objcextern "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:
-
If a properly signed
Frameworkfile is present, openRunner.xcworkspace. - Add the framework file to the Frameworks, Libraries, and Embedded Content section of the target in Xcode.
- Under the Embed column, select Embed & Sign.
Open-source third-party library
#To create a Flutter plugin that includes both C/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_filesfield.
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:
- In your plugin project, open
ios/<myproject>.podspec. -
Add a
vendored_frameworksfield. See the CocoaPods example.
Stripping symbols
#When creating a release build, Xcode strips the symbols.
- In Xcode, select the Runner target, then go to Build Settings > Strip Style.
- 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
#To link against a platform library, use the following instructions:
- In Xcode, open
Runner.xcworkspace. - Select the target platform.
- Click + in the Linked Frameworks and Libraries section.
- 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:
In Xcode, open
Runner.xcworkspace.-
Add the C/C++/Objective-C/Swift source files to the Xcode project.
-
Add the following prefix to the exported symbol declarations to ensure they are visible to Dart:
C/C++/Objective-C:
objcextern "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:
-
If a properly signed
Frameworkfile is present, openRunner.xcworkspace. - Add the framework file to the Frameworks, Libraries, and Embedded Content section of the target in Xcode.
- Under the Embed column, select Embed & Sign.
Compiled (dynamic) library, closed source
#To add a closed source library to a Flutter macOS Desktop app, use the following instructions:
- Follow the instructions for Flutter desktop to create a Flutter desktop app.
-
Open the
yourapp/macos/Runner.xcworkspacein Xcode.- Drag your precompiled library (
libyourlibrary.dylib) intoRunner/Frameworks. - Click
Runnerand go to theBuild Phasestab.- Drag
libyourlibrary.dylibinto theCopy Bundle Resourceslist. - Under
Embed Libraries, checkCode Sign on Copy. - Under
Link Binary With Libraries, set status toOptional. (We use dynamic linking, no need to statically link.)
- Drag
- Click
Runnerand go to theGeneraltab.- Drag
libyourlibrary.dylibinto the Frameworks, Libraries, and Embedded Content list. - Select Embed & Sign.
- Drag
- Click Runner and go to the Build Settings tab.
- In the Search Paths section configure the
Library Search Paths to include the path
where
libyourlibrary.dylibis located.
- In the Search Paths section configure the
Library Search Paths to include the path
where
- Drag your precompiled library (
-
Edit
lib/main.dart.- Use
DynamicLibrary.open('libyourlibrary.dylib')to dynamically link to the symbols. - Call your native function somewhere in a widget.
- Use
- Run
flutter runand check that your native function gets called. -
Run
flutter build macosto build a self-contained release version of your app.
Stripping symbols
#When creating a release build, Xcode strips the symbols.
- In Xcode, select the Runner target, then go to Build Settings > Strip Style.
- Change from All Symbols to Non-Global Symbols.
Android
#Platform library
#To link against a platform library, use the following instructions:
-
Find the desired library in the Android NDK Native APIs list in the Android docs. This lists stable native APIs.
-
Load the library using
DynamicLibrary.open. For example, to load OpenGL ES (v3):dartDynamicLibrary.open('libGLES_v3.so');
You might need to update the Android manifest file of the app or plugin if indicated by the documentation.
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
#
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).
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:
- Open the
android/build.gradlefile for your project. - 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.
Other resources
#To learn more about C interoperability, check out these videos:
Unless stated otherwise, the documentation on this site reflects Flutter 3.44.0. Page last updated on 2026-06-08. View source or report an issue.