C interop using dart:ffi
- Examples
- Walkthrough of hello_world
- Bundling and loading C libraries
- Interfacing with native types
- Generating FFI bindings with package:ffigen
- Building and bundling native assets
Dart mobile, command-line, and server apps
running on the Dart Native platform
can use the dart:ffi
library to call native C APIs,
and to read, write, allocate, and deallocate native memory.
FFI stands for foreign function interface.
Other terms for similar functionality include
native interface and language bindings.
API documentation is available in the
dart:ffi
API reference.
Examples
The following examples show how to use the dart:ffi
library:
Example | Description |
---|---|
hello_world | How to call a C function with no arguments and no return value. |
primitives | How to call C functions that have arguments and return values that are ints or pointers. |
structs | How to use structs to pass strings to and from C and to handle simple and complex C structures. |
sqlite | An example in the Dart SDK repo that comes with a mini tutorial. |
Walkthrough of hello_world
The hello_world example has the minimum necessary code for calling a C library.
Files
The hello_world example has the following files:
Source file | Description |
---|---|
hello.dart | A Dart file that uses the hello_world() function from a C library. |
pubspec.yaml | The Dart pubspec file, with a lower bounds on the SDK that’s at least 2.6. |
hello_library/hello.h | Declares the hello_world() function. |
hello_library/hello.c | A C file that imports hello.h and defines the hello_world() function. |
hello_library/hello.def | A module-definition file which specifies information used when building a DLL. |
hello_library/CMakeLists.txt | A CMake build file for compiling the C code into a dynamic library. |
Building the C library creates several files,
including a dynamic library file named
libhello.dylib
(macOS),
libhello.dll
(Windows), or
libhello.so
(Linux).
Building and running
Here’s an example of building the dynamic library and executing the Dart app:
$ cd hello_library
$ cmake .
...
$ make
...
$ cd ..
$ dart pub get
$ dart run hello.dart
Hello World
Using dart:ffi
The hello.dart
file
illustrates the steps for using dart:ffi
to call a C function:
- Import
dart:ffi
. - Import the path library that you’ll use to store the path of dynamic library.
- Create a typedef with the FFI type signature of the C function.
- Create a typedef for the variable that you’ll use when calling the C function.
- Create a variable to store the path of the dynamic library.
- Open the dynamic library that contains the C function.
- Get a reference to the C function, and put it into a variable.
- Call the C function.
Here’s the code for each step.
- Import
dart:ffi
.import 'dart:ffi' as ffi;
- Import the path library that you’ll use to store the path of dynamic library.
import 'dart:io' show Platform, Directory; import 'package:path/path.dart' as path;
- Create a typedef with the FFI type signature of the C function.
See Interfacing with native types for commonly used types defined bydart:ffi
library.typedef hello_world_func = ffi.Void Function();
- Create a typedef for the variable that you’ll use
when calling the C function.
typedef HelloWorld = void Function();
- Create a variable to store the path of the dynamic library.
var libraryPath = path.join(Directory.current.path, 'hello_library', 'libhello.so'); if (Platform.isMacOS) { libraryPath = path.join(Directory.current.path, 'hello_library', 'libhello.dylib'); } else if (Platform.isWindows) { libraryPath = path.join(Directory.current.path, 'hello_library', 'Debug', 'hello.dll'); }
- Open the dynamic library that contains the C function.
final dylib = ffi.DynamicLibrary.open(libraryPath);
- Get a reference to the C function,
and put it into a variable.
This code uses the typedefs defined in steps 2 and 3,
along with the dynamic library variable from step 4.
final HelloWorld hello = dylib .lookup<ffi.NativeFunction<hello_world_func>>('hello_world') .asFunction();
- Call the C function.
hello();
Once you understand the hello_world example,
you should be ready to look at the
other dart:ffi
examples.
Bundling and loading C libraries
How you bundle (or package or distribute) a C library with your package or app and then load that library depends on your platform and the type of library. For details, see the following:
- Flutter
dart:ffi
pages: Android, iOS, and macOS dart:ffi
examples
Interfacing with native types
The dart:ffi
library provides multiple types
that implement NativeType
and represent native types in C.
Some native types are only used as markers in type signatures while others (or their subtypes) can be instantiated.
Instantiable native types
The following native types can be used as markers in type signatures and they (or their subtypes) can be instantiated in Dart code:
Dart type | Description |
---|---|
Array | A fixed-sized array of items. Supertype of type specific arrays. |
Pointer | Represents a pointer into native C memory. |
Struct | The supertype of all FFI struct types. |
Union | The supertype of all FFI union types. |
Purely marker native types
The following are platform-agnostic native types that are used only as markers in type signatures, and can’t be instantiated in Dart code:
Dart type | Description |
---|---|
Bool | Represents a native bool in C. |
Double | Represents a native 64 bit double in C. |
Float | Represents a native 32 bit float in C. |
Int8 | Represents a native signed 8 bit integer in C. |
Int16 | Represents a native signed 16 bit integer in C. |
Int32 | Represents a native signed 32 bit integer in C. |
Int64 | Represents a native signed 64 bit integer in C. |
NativeFunction | Represents a function type in C. |
Opaque | The supertype of all opaque types in C. |
Uint8 | Represents a native unsigned 8 bit integer in C. |
Uint16 | Represents a native unsigned 16 bit integer in C. |
Uint32 | Represents a native unsigned 32 bit integer in C. |
Uint64 | Represents a native unsigned 64 bit integer in C. |
Void | Represents the void type in C. |
There are also many ABI specific marker native types that extend AbiSpecificInteger. Refer to their linked API documentation for more information and a guideline on what types they map to on specific platforms:
Dart type | Description |
---|---|
AbiSpecificInteger | The supertype of all ABI-specific integer types. |
Int | Represents the int type in C. |
IntPtr | Represents the intptr_t type in C. |
Long | Represents the long int (long ) type in C. |
LongLong | Represents the long long type in C. |
Short | Represents the short type in C. |
SignedChar | Represents the signed char type in C. |
Size | Represents the size_t type in C. |
UintPtr | Represents the uintptr_t type in C. |
UnsignedChar | Represents the unsigned char type in C. |
UnsignedInt | Represents the unsigned int type in C. |
UnsignedLong | Represents the unsigned long int (unsigned long ) type in C. |
UnsignedLongLong | Represents the unsigned long long type in C. |
UnsignedShort | Represents the unsigned short type in C. |
WChar | Represents the wchar_t type in C. |
package:ffigen
Generating FFI bindings with For large API surfaces it can be time-consuming
to write the Dart bindings that integrate with the C code.
To reduce this burden,
you can use the package:ffigen
binding generator
to automatically create FFI wrappers from C header files.
Building and bundling native assets
The Native Assets feature aims to resolve a number of issues associated with the distribution of Dart packages that depend on native code. It does so by providing uniform hooks for integrating with various build systems involved in building Flutter and standalone Dart applications.
The Native Assets feature aims to make it seamless for Dart packages to depend on and use native code:
- It builds (if needed) the native code or
obtains the binaries using a package’s
build.dart
script. - It bundles the native
Asset
reported by thebuild.dart
script. - It makes the native assets available at runtime through
declarative
@Native<>() extern
functions using theassetId
.
The flutter run
/ flutter build
and dart run
/ dart build
tools
will now build and bundle native code when
opted in to the native experiment.
native_add_library
Walkthrough of The native_add_library
example has the minimum necessary code for
building and bundling C code in a Dart package.
The example has the following files:
Source file | Description |
---|---|
src/native_add_library.c |
The C file containing the code for add . |
lib/native_add_library.dart |
The Dart file that invokes the C function add in asset package:native_add_library/native_add_library.dart through FFI. (Note that asset id defaults to the library uri.) |
test/native_add_library_test.dart |
A Dart test using the native code. |
build.dart |
A script for compiling src/native_add_library.c and declaring the compiled asset with id package:native_add_library/native_add_library.dart . |
When a Dart or Flutter project depends on package:native_add_library
,
the build.dart
script will automatically be
invoked on run
, build
, and test
commands.
The native_add_app
example showcases a use of native_add_library
.
API documentation for the native assets in Dart FFI is available in
the dart:ffi
API reference for Native
and DefaultAsset
.
API documentation for the build.dart
script is available
on the package:native_assets_cli
API reference.
Experiment opt-in
For more information on how to enable the experiment and provide feedback, please refer to the tracking issues: