C-Bibliotheken mit dart:ffi aufrufen
Binden Sie native Shared Libraries an und marshallen Sie Structs und Pointer über dart:ffi.
C-Bibliotheken mit dart:ffi aufrufen ist eine kostenlose Flutter Mobile Development-Lektion auf CoddyKit. Dies ist Lektion 1 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Flutter Mobile Development-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Flutter Mobile Development-Kurs umfasst insgesamt 4 Lektionen.
Teile dieser Lektion wurden noch nicht übersetzt und werden auf Englisch angezeigt.
Why dart:ffi?
dart:ffi is Dart's Foreign Function Interface. It lets your Flutter app call functions in native C shared libraries (.so, .dylib, .dll, or the iOS process image) directly, with no platform-channel round-trip.
- Synchronous by default and very low overhead, unlike
MethodChannelwhich serializes messages across an async boundary. - Ideal for CPU-heavy code, existing C/C++/Rust libraries, and OS-level APIs (sqlite, libsodium, image codecs).
- You bind a C signature to a Dart signature, then call it like an ordinary function.
The cost: you manage memory and types yourself. Get a pointer or a struct layout wrong and you crash the whole process.
Opening a DynamicLibrary
Everything starts with a DynamicLibrary. It is the handle to the loaded native code from which you look up symbols.
DynamicLibrary.open(path)loads a shared library by file name. On Android use'libfoo.so'; on iOS/macOS code is usually statically linked, so useDynamicLibrary.process()orDynamicLibrary.executable().- Pick the right name per platform with
Platform.isAndroid/Platform.isIOS.
import 'dart:ffi';
import 'dart:io' show Platform;
DynamicLibrary openNativeLib() {
if (Platform.isAndroid) {
return DynamicLibrary.open('libnative_math.so');
}
if (Platform.isIOS || Platform.isMacOS) {
// Symbols are linked into the app process on iOS.
return DynamicLibrary.process();
}
if (Platform.isWindows) {
return DynamicLibrary.open('native_math.dll');
}
return DynamicLibrary.open('libnative_math.so');
}Native types vs Dart types
FFI uses two type universes. The native type describes the C ABI; the Dart type is what your Dart code actually sees.
Int32,Int64,Uint8,Double,Floatare native marker types — you never instantiate them, they map to Dartint/double.Pointer<T>is a native address.Voidmarks no value.- The C function type is written with
Functionusing native types; the Dart-facing type uses plain Dart types.
Example: C int32_t add(int32_t, int32_t) becomes native Int32 Function(Int32, Int32) and Dart int Function(int, int).
Looking up and calling a function
Use lookupFunction to bind a C symbol to a Dart function in one call. It takes two generic parameters: the native signature and the Dart signature.
- The first type argument must use native types (
Int32,Double, …). - The second is the callable Dart type returned to you.
Below, a pure-Dart simulation shows the call shape that FFI mirrors at runtime.
// Conceptually, FFI does this:
// typedef NativeAdd = Int32 Function(Int32, Int32);
// typedef DartAdd = int Function(int, int);
// final add = lib.lookupFunction<NativeAdd, DartAdd>('add');
// Pure-Dart stand-in so the call site is identical in shape:
int Function(int, int) bindAdd() {
return (int a, int b) => a + b; // native impl returns a + b
}
void main() {
final add = bindAdd();
print('add(20, 22) = ${add(20, 22)}');
}typedef for clean bindings
Real bindings declare the two signatures as typedefs. This keeps lookupFunction readable and lets you reuse signatures.
- Native typedef uses native marker types and the suffix convention
...Native. - Dart typedef uses Dart types.
- The string passed to
lookupFunctionis the exact exported C symbol name.
import 'dart:ffi';
// C: double native_pow(double base, int32_t exp);
typedef NativePowNative = Double Function(Double, Int32);
typedef NativePow = double Function(double, int);
class MathBindings {
final DynamicLibrary lib;
late final NativePow pow;
MathBindings(this.lib) {
pow = lib.lookupFunction<NativePowNative, NativePow>('native_pow');
}
}Allocating native memory
To pass pointers you must allocate native (off-heap) memory. The package:ffi library provides malloc (a calloc variant also exists) plus extensions for strings.
malloc<Int32>()returns aPointer<Int32>; use.valueto read/write.malloc<Int32>(n)allocates an array ofnelements; index withptr[i]orptr.elementAt(i).- You must free what you allocate with
malloc.free(ptr)— the GC does not track native memory.
import 'dart:ffi';
import 'package:ffi/ffi.dart';
void usePointer() {
final ptr = malloc<Int32>(3); // array of 3 int32
try {
ptr[0] = 10;
ptr[1] = 20;
ptr[2] = 12;
var sum = 0;
for (var i = 0; i < 3; i++) {
sum += ptr[i];
}
print('sum = $sum');
} finally {
malloc.free(ptr); // always free
}
}Marshalling strings
C strings are null-terminated char*, represented as Pointer<Utf8> (from package:ffi). Conversion goes both ways:
- Dart → C:
myString.toNativeUtf8()allocates a native buffer (free it later). - C → Dart:
ptr.toDartString()copies the bytes into a DartString.
If the native function returns a pointer it allocated, you typically must call its matching free export — never malloc.free memory you did not allocate with malloc.
import 'dart:ffi';
import 'package:ffi/ffi.dart';
// C: int32_t count_chars(const char* text);
typedef CountNative = Int32 Function(Pointer<Utf8>);
typedef Count = int Function(Pointer<Utf8>);
int countChars(Count nativeCount, String text) {
final cStr = text.toNativeUtf8();
try {
return nativeCount(cStr);
} finally {
malloc.free(cStr);
}
}Defining a Struct
To marshal C structs, declare a Dart class extending Struct. Each field is annotated with its native type so the FFI runtime computes the exact memory layout/offsets.
- Scalar fields get annotations like
@Int32(),@Double(). - Field order and types must match the C struct exactly, including padding/alignment rules.
- You never construct a
Structwithnew; you obtain one via aPointer<T>.refbacked by native memory.
import 'dart:ffi';
// C:
// typedef struct { double x; double y; } Point;
final class Point extends Struct {
@Double()
external double x;
@Double()
external double y;
}Passing structs by pointer
Most C APIs take a Point*. Allocate the struct, fill it through .ref, pass the pointer, then read results back.
malloc<Point>()gives aPointer<Point>sized correctly for the layout.ptr.refis a view onto that native memory; writingptr.ref.x = 3.0mutates the C struct in place.- The native function reads/writes the same memory — this is how you get values out by reference.
import 'dart:ffi';
import 'package:ffi/ffi.dart';
// C: void translate(Point* p, double dx, double dy);
typedef TranslateNative = Void Function(Pointer<Point>, Double, Double);
typedef Translate = void Function(Pointer<Point>, double, double);
final class Point extends Struct {
@Double()
external double x;
@Double()
external double y;
}
void moveOrigin(Translate translate) {
final p = malloc<Point>();
try {
p.ref.x = 0;
p.ref.y = 0;
translate(p, 4.0, 5.0);
print('moved to (${p.ref.x}, ${p.ref.y})');
} finally {
malloc.free(p);
}
}Don't block the UI thread
FFI calls are synchronous: they run on the calling isolate's thread. A long native computation called from the main isolate freezes Flutter's UI.
- For heavy work, run the FFI call inside an
Isolate(e.g.Isolate.runon modern Dart) or a worker isolate. - Note: a
DynamicLibraryhandle and native pointers can be passed between isolates as addresses, but each isolate must re-open or share carefully — treat pointers as plain integers across boundaries. - Native code that calls back into Dart must use
NativeCallable/ send ports, not arbitrary threads.
import 'dart:isolate';
// Simulates offloading a heavy native FFI computation off the UI thread.
int _heavyNativeWork(int n) {
var acc = 0;
for (var i = 0; i < n; i++) {
acc = (acc + i) % 1000003;
}
return acc;
}
Future<void> main() async {
final result = await Isolate.run(() => _heavyNativeWork(5000000));
print('result = $result');
}Memory safety and ownership
FFI bugs are process crashes, not exceptions. Discipline matters:
- Ownership: whoever allocates must free. Memory from
malloc→malloc.free. Memory from a C library → that library's destructor export. - Wrap allocate/use/free in
try/finallyso you free even on error. - For long-lived native objects, attach a
NativeFinalizerso the destructor runs when the Dart wrapper is GC'd. - Never read
.ref/.valueon a pointer after it is freed — that is a use-after-free.
import 'dart:ffi';
import 'package:ffi/ffi.dart';
class SafeBuffer {
final Pointer<Uint8> ptr;
final int length;
SafeBuffer(this.length) : ptr = malloc<Uint8>(length);
void dispose() => malloc.free(ptr);
}
void main() {
final buf = SafeBuffer(16);
try {
buf.ptr[0] = 255;
print('first byte = ${buf.ptr[0]}');
} finally {
buf.dispose();
}
}Quick Check
Answer based on dart:ffi struct and memory rules.
Recap
You can now bind to native C libraries from Flutter with dart:ffi:
- Load code with
DynamicLibrary.open/.process(), choosing the path per platform. - Bind symbols with
lookupFunction<Native, Dart>, declaring native vs Darttypedefs. - Allocate off-heap memory with
malloc, marshal strings viatoNativeUtf8/toDartString, and pass arrays as pointers. - Define structs by extending
Structwith native-type annotations; pass them by pointer and read results through.ref. - Keep heavy calls off the UI isolate, and enforce strict ownership: free what you allocate, use
try/finallyandNativeFinalizer, and never touch freed pointers.
FFI trades safety for speed and reach — correct types, layout, and lifetimes are entirely your responsibility.
Lerne Dart mit einem KI-Tutor — kostenlos
Schreibe und führe echten Code in deinem Browser aus, bekomme sofortige Hilfe von einem 24/7 KI-Tutor und setze dein Lernen im Web oder in der App fort.
- Kurse
- 22
- Lektionen
- 88
Häufig gestellte Fragen
Ist die Lektion „C-Bibliotheken mit dart:ffi aufrufen“ kostenlos?
Ja — der vollständige Text von „C-Bibliotheken mit dart:ffi aufrufen“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Flutter Mobile Development-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Flutter Mobile Development-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „C-Bibliotheken mit dart:ffi aufrufen“?
Binden Sie native Shared Libraries an und marshallen Sie Structs und Pointer über dart:ffi. Du übst Flutter Mobile Development mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um Flutter Mobile Development zu starten?
Keine Vorkenntnisse erforderlich. Flutter Mobile Development auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 1 von 4.
Wie lange dauert die Lektion „C-Bibliotheken mit dart:ffi aufrufen“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser Flutter Mobile Development-Lektion Code schreiben und ausführen?
Ja. Jede Flutter Mobile Development-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- C-Bibliotheken mit dart:ffi aufrufen
- Typsichere Platform Channels mit Pigeon
- Eigene Platform-Plugins für iOS und Android schreiben
- Hintergrund-Isolates und natives Speichermanagement