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:

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. Also demonstrates varargs.
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 usual Dart pubspec, 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/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:

  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.
    import 'dart:ffi' as ffi;
    
  2. 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;
    
  3. Create a typedef with the FFI type signature of the C function.
    Commonly used types defined by dart:ffi library include Double, Int32, NativeFunction, Pointer, Struct, Uint8, and Void.
    typedef hello_world_func = ffi.Void Function();
    
  4. Create a typedef for the variable that you’ll use when calling the C function.
    typedef HelloWorld = void Function();
    
  5. 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');
    } 
    
  6. Open the dynamic library that contains the C function.
      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.
      final HelloWorld hello = dylib
       .lookup<ffi.NativeFunction<hello_world_func>>('hello_world')
       .asFunction();
    
  8. 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:

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.