Anropa C-bibliotek med dart:ffi
Anslut till inbyggda delade bibliotek och överför strukturer och pekare via dart:ffi.
Anropa C-bibliotek med dart:ffi är en gratis lektion i Mobilutveckling med Flutter på CoddyKit. Detta är lektion 1 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Mobilutveckling med Flutter, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Mobilutveckling med Flutter innehåller totalt 4 lektioner.
Varför dart:ffi?
dart:ffi är Darts Foreign Function Interface. Det låter Flutter-appen anropa funktioner direkt i inbyggda delade C-bibliotek (.so, .dylib, .dll eller iOS-processavbildningen), utan någon tur och retur via en plattformskanal.
- Synkront som standard och med mycket liten overhead, till skillnad från
MethodChannel, som serialiserar meddelanden över en asynkron gräns. - Idealiskt för CPU-intensiv kod, befintliga C/C++/Rust-bibliotek och API:er på OS-nivå (sqlite, libsodium, bildkodekar).
- Ni kopplar en C-signatur till en Dart-signatur och anropar den sedan som en vanlig funktion.
Kostnaden är att ni själva måste hantera minne och typer. Om en pekare eller en struct-layout blir fel kraschar hela processen.
Öppna ett DynamicLibrary
Allt börjar med ett DynamicLibrary. Det är handtaget till den inlästa inbyggda koden, där ni slår upp symboler.
DynamicLibrary.open(path)läser in ett delat bibliotek utifrån filnamnet. På Android använder ni'libfoo.so'; på iOS/macOS är koden vanligtvis statiskt länkad, så användDynamicLibrary.process()ellerDynamicLibrary.executable().- Välj rätt namn för varje plattform med
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');
}Inbyggda typer kontra Dart-typer
FFI använder två typvärldar. Den inbyggda typen beskriver C-ABI:t, medan Dart-typen är den som Dart-koden faktiskt ser.
Int32,Int64,Uint8,Double,Floatär inbyggda markörtyper — ni instansierar dem aldrig; de mappas till Dart-typernaint/double.Pointer<T>är en inbyggd adress.Voidmarkerar att inget värde finns.- C-funktionstypen skrivs med
Functionoch använder inbyggda typer; typen som exponeras mot Dart använder vanliga Dart-typer.
Exempel: C int32_t add(int32_t, int32_t) blir den inbyggda typen Int32 Function(Int32, Int32) och Dart-typen int Function(int, int).
Slå upp och anropa en funktion
Använd lookupFunction för att binda en C-symbol till en Dart-funktion i ett enda anrop. Den tar två generiska parametrar: den inbyggda signaturen och Dart-signaturen.
- Det första typargumentet måste använda inbyggda typer (
Int32,Double, …). - Det andra är den anropbara Dart-typ som returneras till er.
Nedanstående simulering i ren Dart visar den anropsform som FFI speglar vid körning.
// 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 för tydliga bindningar
I riktiga bindningar deklarerar ni de två signaturerna som typedefs. Det gör lookupFunction lättare att läsa och låter er återanvända signaturerna.
- Den inbyggda typedefen använder inbyggda markörtyper och suffixkonventionen
...Native. - Dart-typedefen använder Dart-typer.
- Strängen som skickas till
lookupFunctionär det exakta namnet på den exporterade C-symbolen.
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');
}
}Allokera inbyggt minne
För att skicka pekare måste ni allokera inbyggt minne (utanför heapen). Biblioteket package:ffi tillhandahåller malloc (det finns även en variant som heter calloc) samt tillägg för strängar.
malloc<Int32>()returnerar enPointer<Int32>; använd.valueför att läsa och skriva.malloc<Int32>(n)allokerar en array mednelement; indexera medptr[i]ellerptr.elementAt(i).- Ni måste frigöra det ni allokerar med
malloc.free(ptr)— GC spårar inte inbyggt minne.
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
}
}Konvertera strängar
C-strängar är nollterminerade char* och representeras som Pointer<Utf8> (från package:ffi). Konverteringen fungerar åt båda hållen:
- Dart → C:
myString.toNativeUtf8()allokerar en inbyggd buffert (frigör den senare). - C → Dart:
ptr.toDartString()kopierar byten till en Dart-String.
Om den inbyggda funktionen returnerar en pekare som den själv har allokerat måste ni vanligtvis anropa motsvarande exporterade free-funktion — använd aldrig malloc.free på minne som ni inte har allokerat med 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);
}
}Definiera en Struct
För att konvertera C-structar deklarerar ni en Dart-klass som utökar Struct. Varje fält annoteras med sin inbyggda typ, så att FFI-körmiljön kan beräkna den exakta minneslayouten och offsetarna.
- Skalära fält får annoteringar som
@Int32()och@Double(). - Fältens ordning och typer måste exakt överensstämma med C-structen, inklusive reglerna för utfyllnad och justering.
- Ni skapar aldrig en
Structmednew; ni hämtar en via enPointer<T>.refsom stöds av inbyggt minne.
import 'dart:ffi';
// C:
// typedef struct { double x; double y; } Point;
final class Point extends Struct {
@Double()
external double x;
@Double()
external double y;
}Skicka structar via pekare
De flesta C-API:er tar emot en Point*. Allokera structen, fyll i den via .ref, skicka pekaren och läs sedan tillbaka resultaten.
malloc<Point>()ger enPointer<Point>med rätt storlek för layouten.ptr.refär en vy över det inbyggda minnet; när ni skriverptr.ref.x = 3.0ändras C-structen direkt i minnet.- Den inbyggda funktionen läser och skriver i samma minne — det är så ni får ut värden via referens.
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);
}
}Blockera inte UI-tråden
FFI-anrop är synkrona: de körs på tråden för den isolate som anropar dem. En långvarig inbyggd beräkning som anropas från huvudisolaten fryser Flutter-gränssnittet.
- För tungt arbete kör ni FFI-anropet i en
Isolate(till exempel medIsolate.runi moderna Dart-versioner) eller i en worker-isolate. - Observera att ett
DynamicLibrary-handtag och inbyggda pekare kan skickas mellan isolater som adresser, men varje isolate måste öppna biblioteket igen eller dela det med försiktighet — behandla pekare som vanliga heltal över gränserna. - Inbyggd kod som anropar tillbaka till Dart måste använda
NativeCallable/ send ports, inte godtyckliga trådar.
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');
}Minnessäkerhet och ägarskap
FFI-buggar leder till processkrascher, inte undantag. Disciplin är viktigt:
- Ägarskap: den som allokerar måste frigöra. Minne från
malloc→malloc.free. Minne från ett C-bibliotek → destruktorn som exporteras av det biblioteket. - Omslut allokering, användning och frigöring i
try/finally, så att minnet frigörs även vid fel. - För långlivade inbyggda objekt kan ni koppla en
NativeFinalizerså att destruktorn körs när Dart-wrappen samlas in av GC. - Läs aldrig
.ref/.valuefrån en pekare efter att den har frigjorts — det är 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();
}
}Snabbkontroll
Svara utifrån reglerna för structar och minne i dart:ffi.
Sammanfattning
Ni kan nu binda Flutter till inbyggda C-bibliotek med dart:ffi:
- Läs in kod med
DynamicLibrary.open/.process()och välj sökväg per plattform. - Bind symboler med
lookupFunction<Native, Dart>och deklarera inbyggda typedefar respektive Dart-typedefar. - Allokera minne utanför heapen med
malloc, konvertera strängar viatoNativeUtf8/toDartStringoch skicka arrayer som pekare. - Definiera structar genom att utöka
Structmed annoteringar för inbyggda typer; skicka dem via pekare och läs resultat via.ref. - Håll tunga anrop borta från UI-isolaten och upprätthåll strikt ägarskap: frigör det ni allokerar, använd
try/finallyochNativeFinalizer, och använd aldrig frigjorda pekare.
FFI byter säkerhet mot snabbhet och räckvidd — korrekta typer, layouter och livslängder är helt och hållet ert ansvar.
Lär dig Dart med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 22
- Lektioner
- 88
Vanliga frågor
Är lektionen ”Anropa C-bibliotek med dart:ffi” gratis?
Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Mobilutveckling med Flutter, inklusive ”Anropa C-bibliotek med dart:ffi”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Mobilutveckling med Flutter innehåller totalt 4 lektioner.
Vad lär jag mig i ”Anropa C-bibliotek med dart:ffi”?
Anslut till inbyggda delade bibliotek och överför strukturer och pekare via dart:ffi. Ni övar på Mobilutveckling med Flutter med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Mobilutveckling med Flutter?
Du behöver inga förkunskaper. Utbildningen i Mobilutveckling med Flutter på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.
Hur lång tid tar lektionen ”Anropa C-bibliotek med dart:ffi”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Mobilutveckling med Flutter-lektionen?
Ja. Varje Mobilutveckling med Flutter-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Anropa C-bibliotek med dart:ffi
- Typsäkra plattformskanaler med Pigeon
- Skriv anpassade plattformspluginer för iOS och Android
- Bakgrundsisolate och hantering av inbyggt minne