FFI: بنیادی باتیں، Foreign Function Interface اور C کے ساتھ انضمام

مصنف: IT Sectr اشاعت: 2026-06-05 مطالعے کا وقت: 10 منٹ

FFI (Foreign Function Interface) Dart زبان کا ایک طریقہ کار ہے، جو dart:ffi پیکیج کے ذریعے فراہم کیا گیا ہے، جو Kotlin، Swift یا Java میں درمیانی تہوں کے بغیر براہ راست مقامی C لائبریریوں سے فنکشنز کال کرنے کی اجازت دیتا ہے۔ ڈویلپر ایک متحرک لائبریری (.so Android پر، .dylib iOS پر، .dll Windows پر) لوڈ کرتا ہے، C فنکشن کے دستخطوں کا اعلان کرتا ہے اور انہیں عام Dart فنکشنز کی طرح کال کرتا ہے۔ Dart API Reference (2025) کے مطابق، FFI بین لسانی کالوں کے اوورہیڈ کو 0.1 µs تک کم کرتا ہے، جو Method Channel سے دسیوں گنا تیز ہے۔

اہم نکات

  • FFI (Foreign Function Interface) — Dart سے براہ راست C فنکشنز کال کرنے کا طریقہ کار
  • dart:ffi لائبریریاں لوڈ کرنے اور دستخطوں کے اعلان کے لیے API فراہم کرتا ہے
  • کارکردگی FFI کالز کی Method Channel سے 50–100 گنا زیادہ ہے
  • ٹائپنگ FFI قدیمی اقسام، ڈھانچے اور C پوائنٹرز کو سپورٹ کرتا ہے
  • Flutter مقامی لائبریریوں: OpenCV، SQLite، FFmpeg کے ساتھ انضمام کے لیے FFI استعمال کرتا ہے

FFI کیا ہے؟

FFI (Foreign Function Interface) ایک طریقہ کار ہے جو پروگرامنگ زبان کو دوسری زبانوں میں لکھے گئے فنکشنز کال کرنے کی اجازت دیتا ہے۔ Dart اور Flutter کے سیاق و سباق میں، FFI کا مطلب Java (Android) یا Swift/Objective-C (iOS) میں پلیٹ فارم مخصوص کوڈ لکھے بغیر براہ راست Dart کوڈ سے C/C++ لائبریریوں کے فنکشنز کال کرنے کی صلاحیت ہے۔

dart:ffi پیکیج Dart 2.12 (2021) میں ظاہر ہوا اور تب سے Flutter کو مقامی کوڈ کے ساتھ مربوط کرنے کے لیے ایک اہم آلہ بن گیا ہے۔ dart:ffi سے پہلے، Dart سے C فنکشن کال کرنے کا واحد راستہ Method Channel تھا — ایک غیر متزامن طریقہ کار جو Dart اور مقامی جانب کے درمیان JSON سیریلائزیشن کے ذریعے پیغامات منتقل کرتا تھا۔ FFI مختلف طریقے سے کام کرتا ہے: Dart کوڈ براہ راست C لائبریری میموری تک رسائی حاصل کرتا ہے، سیریلائزیشن یا سیاق و سباق کی تبدیلی کے بغیر مقامی ABI (Application Binary Interface) کے ذریعے فنکشنز کال کرتا ہے۔

FFI خاص طور پر ان منظرناموں میں مانگ میں ہے جہاں کارکردگی اہم ہے: تصویری پروسیسنگ (OpenCV)، آڈیو (FFmpeg)، کرپٹوگرافی (OpenSSL)، مشین لرننگ (TensorFlow Lite) اور ڈیٹا بیس (SQLite)۔ ان تمام صورتوں میں، Method Channel ناقابل قبول تاخیر پیدا کرتا ہے، جبکہ FFI مقامی C/C++ کوڈ کے برابر کارکردگی فراہم کرتا ہے۔ dart:ffi لائبریری میموری مینجمنٹ کو بھی سپورٹ کرتی ہے: مختص، آزادی اور پوائنٹر آپریشنز۔

FFI بمقابلہ Method Channel: بنیادی فرق

Method Channel غیر متزامن طور پر کام کرتا ہے: Dart مقامی کوڈ کو ایک پیغام بھیجتا ہے، مقامی کوڈ اسے پروسیس کرتا ہے اور نتیجہ واپس بھیجتا ہے۔ ہر کال میں Map میں آرگیومینٹس کی سیریلائزیشن، قطار کے ذریعے گزرنا اور ڈی سیریلائزیشن کی ضرورت ہوتی ہے۔ اس میں فی کال 0.5–5 ms لگتے ہیں۔ FFI متزامن طور پر اور سیریلائزیشن کے بغیر کام کرتا ہے — C فنکشن کال میں 0.01–0.1 µs لگتے ہیں۔ 50–500 گنا کا فرق، جو زیادہ تعدد والی کارروائیوں کے لیے اہم ہے۔

dart:ffi کیسے کام کرتا ہے؟

dart:ffi کے ساتھ کام تین مراحل پر مشتمل ہے: لائبریری لوڈ کرنا، دستخطوں کا اعلان کرنا اور فنکشنز کال کرنا۔ ہر مرحلہ Dart کی سخت ٹائپنگ کا استعمال کرتا ہے، جس سے رن ٹائم کی غلطیاں کم ہوتی ہیں۔

پہلے مرحلے میں، DynamicLibrary کلاس کے ذریعے متحرک لائبریری لوڈ کی جاتی ہے۔ لائبریری کو نام (libxyz.so، libxyz.dylib، xyz.dll) یا مکمل راستے سے لوڈ کیا جا سکتا ہے۔ Dart خود بخود سسٹم کے معیاری راستوں میں لائبریری تلاش کرتا ہے۔ DynamicLibrary lookupFunction طریقہ فراہم کرتا ہے، جو علامت کے نام سے Dart فنکشن کو C فنکشن سے باندھتا ہے۔

دوسرے مرحلے میں، C دستخط کے مطابق قسم کے نوٹیشنز کے ساتھ ایک Dart فنکشن کا اعلان کیا جاتا ہے۔ dart:ffi سے خصوصی اقسام استعمال کی جاتی ہیں: Int32، Float، Double، Pointer، NativeFunction، Handle اور دیگر۔ lookupFunction نوٹیشن دو عام پیرامیٹرز لیتا ہے: Dart فنکشن کی قسم (Dart میں کیسی نظر آئے گی) اور مقامی C فنکشن کی قسم (C میں کیسے اعلان کیا گیا ہے)۔

تیسرے مرحلے میں، پیدا کردہ Dart فنکشن کو عام فنکشن کی طرح کال کیا جاتا ہے۔ آرگیومینٹس براہ راست منتقل کیے جاتے ہیں، نتیجہ فوری طور پر واپس آتا ہے۔ اگر C فنکشن پوائنٹرز کے ذریعے میموری کو تبدیل کرتا ہے، تو Dart Pointer کلاس کے ذریعے ان تبدیلیوں کو پڑھ سکتا ہے۔ C طرف کی میموری مینجمنٹ ڈویلپر کی ذمہ داری ہے — dart:ffi C میں malloc کے ذریعے مختص کردہ میموری کا انتظام نہیں کرتا ہے۔

بنیادی مثال: Dart سے C فنکشن کال کرنا

dart
import 'dart:ffi'
import 'package:ffi/ffi.dart'

// C فنکشن کا اعلان: int add(int a, int b)
typedef AddNative = Int32 Function(Int32, Int32)
typedef AddDart = int Function(int, int)

void main() {
    final lib = DynamicLibrary.open('libcalculator.so')
    final AddDart add = lib
        .lookupFunction<AddNative, AddDart>('add')

    print(add(5, 3)) // 8
}

اس مثال میں، add ایک C فنکشن ہے جو دو int لیتا ہے اور ایک int واپس کرتا ہے۔ AddNative typedef dart:ffi اقسام کے ساتھ C دستخط کی وضاحت کرتا ہے، جبکہ AddDart وضاحت کرتا ہے کہ یہ فنکشن Dart میں کیسا نظر آئے گا۔ lookupFunction انہیں باندھتا ہے اور ایک Dart فنکشن واپس کرتا ہے جسے عام کی طرح کال کیا جا سکتا ہے۔

FFI میں ڈیٹا کی اقسام

dart:ffi C اقسام کے مطابق اقسام کا ایک سیٹ فراہم کرتا ہے۔ ہر قسم کا ایک مقررہ سائز اور Dart اور C کے درمیان تبدیلی کے قواعد ہوتے ہیں۔ FFI کے صحیح آپریشن کے لیے قسم کی میپنگ کو سمجھنا بہت اہم ہے — قسم کے سائز یا علامت میں غلطی ایپلیکیشن کو کریش کر سکتی ہے۔

C قسمdart:ffi قسمDart قسمسائز (بائٹس)
intInt32int4
longInt64int8
floatFloatdouble4
doubleDoubledouble8
char*Pointer<Int8>Pointer8 (پوائنٹر)
void*Pointer<Void>Pointer8 (پوائنٹر)
structPointer<T> (Struct)Pointerفیلڈز پر منحصر ہے

C سٹرنگز (char*) کے ساتھ کام کرنے کے لیے، dart:ffi Pointer<Int8> استعمال کرتا ہے۔ Dart String سے C char* اور واپس تبدیلی toNativeUtf8 (ffi پیکیج سے) اور fromUtf8 کے ذریعے کی جاتی ہے۔ میموری لیک سے بچنے کے لیے استعمال کے بعد C سٹرنگز کو calloc.free کے ذریعے آزاد کرنا ضروری ہے۔

ڈھانچے (Struct)

dart:ffi C ڈھانچوں کو Dart کلاسز کے طور پر اعلان کرنے کی حمایت کرتا ہے جو Struct کو بڑھاتے ہیں۔ ڈھانچے کے فیلڈز @Int32()، @Float()، @Array() اور دیگر نوٹیشنز کے ساتھ اعلان کیے جاتے ہیں۔ فیلڈز کا سائز اور آفسیٹ پلیٹ فارم کے ABI کے مطابق خود بخود حساب کیا جاتا ہے۔ Pointer<Point> C فنکشن سے حاصل کیا جا سکتا ہے جو ڈھانچے پر پوائنٹر واپس کرتا ہے، یا Dart میں calloc کے ذریعے مختص کیا جا سکتا ہے۔

dart
// C ڈھانچہ: typedef struct { int x; int y; } Point;
final class Point extends Struct {
    @Int32()
    external int x

    @Int32()
    external int y
}

// C فنکشن کال کرنا جو Point* واپس کرتا ہے
typedef CreatePointNative = Pointer<Point> Function(Int32, Int32)
typedef CreatePointDart = Pointer<Point> Function(int, int)

final Pointer<Point> p = createPoint(10, 20)
print('x: ${p.ref.x}, y: ${p.ref.y}')
calloc.free(p) // میموری آزاد کریں

Point کلاس Struct کو بڑھاتا ہے اور @Int32() نوٹیشنز کے ساتھ x اور y فیلڈز کا اعلان کرتا ہے۔ پیدا کردہ C کوڈ میں بالکل وہی میموری لے آؤٹ ہوگا۔ Pointer.ref گیٹرز اور سیٹرز کے ذریعے ڈھانچے کے فیلڈز تک رسائی فراہم کرتا ہے۔

FFI کی عملی مثالیں

آئیے ایک زیادہ پیچیدہ مثال دیکھتے ہیں — SHA256 ہیش کی گنتی کے لیے C لائبریری کے ساتھ انضمام۔ یہ ایک عام کام ہے جہاں FFI Method Channel کے مقابلے میں نمایاں کارکردگی کا فائدہ فراہم کرتا ہے۔

FFI کے ذریعے OpenSSL کے ساتھ انضمام

OpenSSL لائبریری SHA256 فنکشن فراہم کرتی ہے، جو سٹرنگ کا ہیش شمار کرتی ہے۔ dart:ffi کے ذریعے، ہم Java یا Swift ریپر لکھے بغیر اسے براہ راست کال کر سکتے ہیں۔ یہ ایک مثال ہے کہ کس طرح FFI Flutter میں موجودہ C لائبریریوں کے دوبارہ استعمال کی اجازت دیتا ہے۔

dart
import 'dart:ffi'
import 'package:ffi/ffi.dart'

// دستخط: unsigned char* SHA256(
//   const unsigned char *d, size_t n, unsigned char *md)
typedef Sha256Native = Pointer<Uint8> Function(
    Pointer<Uint8>, Size, Pointer<Uint8>)
typedef Sha256Dart = Pointer<Uint8> Function(
    Pointer<Uint8>, int, Pointer<Uint8>)

String sha256(String input) {
    final lib = DynamicLibrary.open('libcrypto.so')
    final Sha256Dart sha256Fn = lib
        .lookupFunction<Sha256Native, Sha256Dart>('SHA256')

    final inputPtr = input.toNativeUtf8()
    final outputPtr = calloc(Uint8)(32) // SHA256 = 32 بائٹس

    sha256Fn(inputPtr, input.length, outputPtr)

    final digest = outputPtr.asTypedList(32)
    final hex = digest.map((b) => b.toRadixString(16)
        .padLeft(2, '0')).join()

    calloc.free(inputPtr)
    calloc.free(outputPtr)

    return hex
}

اس مثال میں، sha256 فنکشن libcrypto.so لائبریری لوڈ کرتا ہے، SHA256 علامت ڈھونڈتا ہے اور ان پٹ اور آؤٹ پٹ ڈیٹا کے پوائنٹرز کے ساتھ اسے کال کرتا ہے۔ toNativeUtf8 Dart String کو C سٹرنگ میں تبدیل کرتا ہے (میموری مختص کرتا ہے)، اور asTypedList نتیجے کی بائٹ صف کو پڑھنے کی اجازت دیتا ہے۔ استعمال کے بعد میموری آزاد کی جاتی ہے — لیک کو روکنے کے لیے یہ لازمی قدم ہے۔

میموری کی مختص اور آزادی

ffi پیکیج C-مطابق میموری مختص کرنے کے لیے calloc فنکشن فراہم کرتا ہے۔ مختص کردہ میموری کو calloc.free کے ذریعے آزاد کرنا چاہیے، ورنہ لیک ہوگی۔ خودکار میموری مینجمنٹ کے لیے، آپ ffi پیکیج سے Arena کلاس استعمال کر سکتے ہیں، جو arena.release() کال کرنے پر اس کے اندر مختص کردہ تمام میموری آزاد کر دیتی ہے۔ یہ خاص طور پر بڑی تعداد میں عارضی مختص کے لیے آسان ہے۔

حدود اور بہترین طریقہ کار

FFI کی طاقت کے باوجود، اس کی حدود ہیں جنہیں Flutter ایپلیکیشن آرکیٹیکچر ڈیزائن کرتے وقت مدنظر رکھنا چاہیے۔ اہم حدود کا تعلق قسم کی حفاظت، میموری مینجمنٹ اور پلیٹ فارم مطابقت سے ہے۔

حفاظت

FFI رن ٹائم پر اقسام کی جانچ نہیں کرتا ہے۔ اگر C فنکشن پوائنٹر کی توقع کرتا ہے لیکن ایک نمبر وصول کرتا ہے، تو ایپلیکیشن سیگمنٹیشن فالٹ کے ساتھ کریش ہوجائے گی۔ FFIgen استعمال کرنے کی سفارش کی جاتی ہے — ایک ٹول جو C ہیڈر فائلوں (.h) کی بنیاد پر قسم سے محفوظ Dart ریپر تیار کرتا ہے۔ FFIgen C فنکشن کے اعلانات کا تجزیہ کرتا ہے اور صحیح اقسام کے ساتھ Dart کوڈ بناتا ہے، کوڈ لکھنے کے مرحلے میں غلطیوں کو ختم کرتا ہے۔

پلیٹ فارم مطابقت

متحرک لائبریریوں کے نام اور راستے پلیٹ فارمز کے درمیان مختلف ہوتے ہیں: Android/Linux پر libxyz.so، iOS/macOS پر libxyz.dylib، Windows پر xyz.dll۔ کراس پلیٹ فارم لائبریریوں کے لیے، dart:io (Platform.isAndroid، Platform.isIOS) یا package:ffi جیسے تجریدات کے ذریعے مشروط کمپائلیشن استعمال کیا جاتا ہے۔ ایک فیکٹری طریقہ بنانے کی سفارش کی جاتی ہے جو موجودہ پلیٹ فارم کے لیے صحیح لائبریری واپس کرے۔

میموری مینجمنٹ

FFI C طرف کی میموری کا انتظام نہیں کرتا ہے۔ اگر C فنکشن malloc کے ذریعے میموری مختص کرتا ہے، تو اسے free کے ذریعے آزاد کرنا چاہیے، ورنہ لیک ہوگی۔ Dart میں C میموری کے لیے کوئی کوڑا کرکٹ جمع کرنے والا نہیں ہے۔ سفارش: ہمیشہ میموری کو اسی طریقہ میں آزاد کریں جہاں اسے مختص کیا گیا تھا، یا گروپ آزادی کے لیے Arena استعمال کریں۔

کارکردگی اور تھریڈز

FFI کالز Dart کوڈ کے ساتھ اسی تھریڈ میں انجام پاتی ہیں۔ طویل متزامن آپریشنز (10 ms سے زیادہ) UI تھریڈ کو بلاک کرتے ہیں اور فریم ڈراپ کا سبب بنتے ہیں۔ طویل آپریشنز کے لیے، C فنکشن کو Isolate میں کال کرنا چاہیے یا یقینی بنانا چاہیے کہ C فنکشن پس منظر کے تھریڈ میں کام چلاتا ہے اور Port یا کال بیک کے ذریعے Dart کو مطلع کرتا ہے۔

dart
// طویل آپریشنز کے لیے isolate میں FFI
import 'dart:isolate'

Future<String> computeHash(String input) async {
    final port = ReceivePort()
    await Isolate.spawn((SendPort sendPort) {
        final result = sha256(input) // FFI کال
        sendPort.send(result)
    }, port.sendPort)

    return await port.first as String
}

FFI کالز کو isolate میں منتقل کرنا یقینی بناتا ہے کہ UI تھریڈ بلاک نہ ہو۔ تاہم، یہ خیال رکھنا چاہیے کہ isolates کے درمیان ڈیٹا کی بڑی مقدار کی منتقلی کے لیے میموری کاپی کرنے کی ضرورت ہوتی ہے۔ بڑے بفرز (>10 MB) کے لیے، SharedMemory یا میموری میپڈ فائلیں استعمال کرنا بہتر ہے۔

اکثر پوچھے گئے سوالات

FFI Method Channel سے کیسے مختلف ہے؟

FFI C فنکشنز کو براہ راست، متزامن طور پر اور سیریلائزیشن کے بغیر کال کرتا ہے — تاخیر 0.01–0.1 µs۔ Method Channel JSON سیریلائزیشن کے ذریعے غیر متزامن طور پر 0.5–5 ms تاخیر کے ساتھ کام کرتا ہے۔ FFI اعلی کارکردگی والے آپریشنز کے لیے موزوں ہے، Method Channel سادہ پلیٹ فارم API کالز کے لیے۔

کیا FFI کے ذریعے C++ فنکشنز کال کیے جا سکتے ہیں؟

براہ راست — نہیں، dart:ffi صرف C فنکشنز کو سپورٹ کرتا ہے۔ C++ کال کرنے کے لیے، آپ کو extern “C” کے ساتھ C ریپر بنانا ہوگا (انٹری پوائنٹس جو C علامتوں کے طور پر ایکسپورٹ ہوتے ہیں)۔ C++ کلاسز کو ایک اضافی پرت کی ضرورت ہوتی ہے جو طریقہ کالز کو C فنکشنز میں ترجمہ کرتی ہے۔

C فنکشنز میں غلطیوں کو کیسے ہینڈل کریں؟

FFI استثنیات کو سپورٹ نہیں کرتا ہے — اگر C فنکشن غلطی کا کوڈ واپس کرتا ہے، تو اسے دستی طور پر چیک کرنا چاہیے۔ Dart میں FFI کالز کو try-catch میں لپیٹنے اور C فنکشنز کے واپسی کوڈز چیک کرنے کی سفارش کی جاتی ہے۔ سنگین غلطیاں (segfault) پکڑی نہیں جا سکتیں۔

FFI کے ذریعے کون سی لائبریریاں استعمال نہیں کی جا سکتیں؟

FFI ان لائبریریوں کے ساتھ کام نہیں کرتا جنہیں پیچیدہ Java (JNI) یا Objective-C (Message Dispatch) ابتدا کی ضرورت ہوتی ہے۔ مثال کے طور پر، UIKit اور Android Views FFI کے ذریعے قابل رسائی نہیں ہیں۔ حدود کا تعلق اس حقیقت سے ہے کہ FFI C ABI کی سطح پر کام کرتا ہے، جبکہ ان API کو مخصوص رن ٹائم کی ضرورت ہوتی ہے۔

کیا C لائبریریوں کو ہر پلیٹ فارم کے لیے کمپائل کرنے کی ضرورت ہے؟

ہاں، C لائبریریاں ہر ہدف پلیٹ فارم کے لیے الگ الگ کمپائل کی جاتی ہیں۔ Android کے لیے، .so مختلف ABI (armeabi-v7a، arm64-v8a، x86_64) کے لیے بنایا جاتا ہے۔ iOS کے لیے — ایک یونیورسل .dylib (arm64)۔ Windows کے لیے — .dll۔ Flutter بلڈ کے دوران خود بخود لائبریری کے صحیح ورژن کو پیکیج کرتا ہے۔

خلاصہ

  • FFI (Foreign Function Interface) — dart:ffi کے ذریعے Dart سے براہ راست C فنکشنز کال کرنے کا طریقہ کار
  • کارکردگی FFI کالز کی Method Channel سے 50–500 گنا زیادہ ہے
  • آرکیٹیکچر میں لائبریری لوڈ کرنا، دستخطوں کا اعلان اور فنکشن کالز شامل ہیں
  • ڈیٹا کی اقسام dart:ffi Int32، Float، Double، Pointer، Struct اور دیگر C اقسام کو سپورٹ کرتا ہے
  • میموری C طرف calloc/free یا Arena کے ذریعے دستی طور پر منظم کی جاتی ہے
  • حدود FFI: کوئی رن ٹائم قسم کی جانچ نہیں، کوئی براہ راست C++ سپورٹ نہیں، UI تھریڈ کو بلاک کرتا ہے
  • استعمال کریں Flutter میں مقامی لائبریریوں کے ساتھ اعلی کارکردگی والے انضمام کے لیے FFI

ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے

IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔

پروجیکٹ پر بحث کریں

مزید پڑھیں