Contents

C interop using dart:ffi

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:

ExampleDescription
hello_worldHow to call a C function with no arguments and no return value.
primitivesHow to call C functions that have arguments and return values that are ints or pointers.
structsHow to use structs to pass strings to and from C and to handle simple and complex C structures.
sqliteAn 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 fileDescription
hello.dartA Dart file that uses the hello_world() function from a C library.
pubspec.yamlThe Dart pubspec file, with a lower bounds on the SDK that's at least 2.6.
hello_library/hello.hDeclares the hello_world() function.
hello_library/hello.cA C file that imports hello.h and defines the hello_world() function.
hello_library/hello.defA module-definition file which specifies information used when building a DLL.
hello_library/CMakeLists.txtA 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:

  1. Import dart:ffi.
  2. Import the path library that you'll use to store the path of dynamic library.
  3. Create a typedef with the FFI type signature of the C function.
  4. Create a typedef for the variable that you'll use when calling the C function.
  5. Create a variable to store the path of the dynamic library.
  6. Open the dynamic library that contains the C function.
  7. Get a reference to the C function, and put it into a variable.
  8. Call the C function.

Here's the code for each step.

  1. Import dart:ffi.

    dart
    import 'dart:ffi' as ffi;
  2. Import the path library that you'll use to store the path of dynamic library.

    dart
    import 'dart:io' show Platform, Directory;
    import 'package:path/path.dart' as path;
  3. Create a typedef with the FFI type signature of the C function.
    See Interfacing with native types for commonly used types defined by dart:ffi library.

    dart
    typedef hello_world_func = ffi.Void Function();
  4. Create a typedef for the variable that you'll use when calling the C function.

    dart
    typedef HelloWorld = void Function();
  5. Create a variable to store the path of the dynamic library.

    dart
    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');
    }
  6. Open the dynamic library that contains the C function.

    dart
    final dylib = ffi.DynamicLibrary.open(libraryPath);
  7. 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.

    dart
    final HelloWorld hello = dylib
        .lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
        .asFunction();
  8. Call the C function.

    dart
    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:

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 typeDescription
ArrayA fixed-sized array of items. Supertype of type specific arrays.
PointerRepresents a pointer into native C memory.
StructThe supertype of all FFI struct types.
UnionThe 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 typeDescription
BoolRepresents a native bool in C.
DoubleRepresents a native 64 bit double in C.
FloatRepresents a native 32 bit float in C.
Int8Represents a native signed 8 bit integer in C.
Int16Represents a native signed 16 bit integer in C.
Int32Represents a native signed 32 bit integer in C.
Int64Represents a native signed 64 bit integer in C.
NativeFunctionRepresents a function type in C.
OpaqueThe supertype of all opaque types in C.
Uint8Represents a native unsigned 8 bit integer in C.
Uint16Represents a native unsigned 16 bit integer in C.
Uint32Represents a native unsigned 32 bit integer in C.
Uint64Represents a native unsigned 64 bit integer in C.
VoidRepresents 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 typeDescription
AbiSpecificIntegerThe supertype of all ABI-specific integer types.
IntRepresents the int type in C.
IntPtrRepresents the intptr_t type in C.
LongRepresents the long int (long) type in C.
LongLongRepresents the long long type in C.
ShortRepresents the short type in C.
SignedCharRepresents the signed char type in C.
SizeRepresents the size_t type in C.
UintPtrRepresents the uintptr_t type in C.
UnsignedCharRepresents the unsigned char type in C.
UnsignedIntRepresents the unsigned int type in C.
UnsignedLongRepresents the unsigned long int (unsigned long) type in C.
UnsignedLongLongRepresents the unsigned long long type in C.
UnsignedShortRepresents the unsigned short type in C.
WCharRepresents the wchar_t type in C.

Generating FFI bindings with package:ffigen

#

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 the build.dart script.
  • It makes the native assets available at runtime through declarative @Native<>() extern functions using the assetId.

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.

Walkthrough of native_add_library

#

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 fileDescription
src/native_add_library.cThe C file containing the code for add.
lib/native_add_library.dartThe 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.dartA Dart test using the native code.
build.dartA 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: