CodeBlanks Logo Icon CodeBlanks

FFI

Write native integration with dart:ffi.

FFI

dart:ffi lets Dart code call C-based native libraries. A DynamicLibrary is loaded, lookupFunction finds a symbol, and typedefs pair Dart and native signatures.

Two typedefs: NativeAdd = Int32 Function(Int32, Int32) (native signature) and DartAdd = int Function(int, int) (Dart signature). lookupFunction<NativeAdd, DartAdd>('native_add') bridges them.

In this lesson we define NativeMathService implements MathService. The add method wraps the native call; describe returns the binding name. A real native call needs a .so/.dll; here we write a simulated implementation.

Native and Dart signatures

In FFI each native function needs two typedefs: the native side uses types like Int32, Pointer; the Dart side uses int, double.

Example: typedef NativeAdd = Int32 Function(Int32 a, Int32 b); and typedef DartAdd = int Function(int a, int b);. lookupFunction<NativeAdd, DartAdd> pairs them.

In this lesson we write add as a simulated a + b. In a real project this would call the function returned by lookupFunction. The contract stays the same.

Practice Add NativeMathService

The MathService is already defined below. Now add import 'dart:ffi';. Define typedef NativeAdd = Int32 Function(Int32 a, Int32 b); and typedef DartAdd = int Function(int a, int b);. Define class NativeMathService implements MathService with final String bindingName; NativeMathService(this.bindingName); @override int add(int a, int b) => a + b; String describe() => 'native: $bindingName';. In main create final svc = NativeMathService('native_add');; add print(svc.describe()); and print('result: ${svc.add(2, 3)}');.

Starter code

main.dart
abstract class MathService {
  int add(int a, int b);
}

// add dart:ffi import; define NativeAdd and DartAdd typedefs
// define a NativeMathService class (applies the MathService interface)

void main() {
  print('service ready');
  // create a NativeMathService; print describe and add(2,3)
}

Sample solution

main.dart
import 'dart:ffi';

abstract class MathService {
  int add(int a, int b);
}

typedef NativeAdd = Int32 Function(Int32 a, Int32 b);
typedef DartAdd = int Function(int a, int b);

class NativeMathService implements MathService {
  final String bindingName;
  NativeMathService(this.bindingName);
  @override
  int add(int a, int b) => a + b;
  String describe() => 'native: $bindingName';
}

void main() {
  print('service ready');
  final svc = NativeMathService('native_add');
  print(svc.describe());
  print('result: ${svc.add(2, 3)}');
}

Expected output

service ready
native: native_add
result: 5

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Implementing the interface

Question
class NativeMathService ____ MathService { }
Answer
class NativeMathService implements MathService { }

Explanation: implements applies the MathService interface.

2 What can you do with dart:ffi?

  • Calling native C functions Correct answer
  • JSON serialization
  • HTTP requests
  • File reading

Explanation: dart:ffi calls native C functions.

3 Match the FFI parts

Term Meaning
dart:ffi Native interop
DynamicLibrary Library loader
lookupFunction Symbol lookup
Int32 Native integer

All lessons in Expert