Floor — ما هو، ORM فوق SQLite في Flutter

المؤلف: IT Sectr نُشر: 2026-03-13 وقت القراءة: 9 دق

Floor — ORM (Object-Relational Mapping) لـ Flutter، يوفر طبقة مكتوبة فوق SQLite. على عكس استعلامات SQLite الخام، يقوم Floor بتوليد فئات DAO من نماذج Dart المُعلَّمة. وفقًا لـ Pub.dev، 2024، يتم استخدام Floor في أكثر من 3500 مشروع Flutter وهو من بين أشهر ثلاث حلول ORM للتخزين المحلي للبيانات إلى جانب drift و hive.

أهم النقاط

  • Floor — ORM فوق SQLite مع توليد كود DAO و Entity عبر التعليقات التوضيحية
  • أمان الأنواع — يتم فحص الاستعلامات في وقت الترجمة، مما يستبعد أخطاء SQL في وقت التشغيل
  • نمط DAO — يغلف Data Access Object استعلامات SQL في طرق Dart
  • الترحيل — دعم مدمج لإصدار مخطط SQLite
  • التفاعلية — استعلامات التدفق عبر Stream لتحديث واجهة المستخدم تلقائيًا

ما هو Floor؟

Floor هي مكتبة ORM لـ Flutter و Dart مبنية على SQLite. تستخدم التعليقات التوضيحية لوصف الكيانات (Entity) وكائنات الوصول إلى البيانات (DAO) وقاعدة البيانات (Database). يتم توليد الكود عبر build_runner و floor_generator — حيث يقوم المترجم بإنشاء تطبيقات DAO وفئة إدارة قاعدة البيانات. على عكس sqflite الخام، يلغي Floor تمامًا الحاجة إلى التحويل اليدوي من ResultSet إلى كائنات Dart، عن طريق تعيين الأعمدة تلقائيًا إلى حقول Entity عبر انعكاس الأنواع.

يتبع Floor نمط Repository + DAO المألوف لمطوري Android من Room. يتم تمثيل كل جدول بفئة Dart مع التعليق التوضيحي @Entity، ويتم تجميع استعلامات SQL في واجهات مع التعليق التوضيحي @dao، ويتم تجميع قاعدة البيانات في فئة مجردة مع @Database. يفصل هذا النهج بدقة نموذج البيانات عن منطق الاستعلامات.

وفقًا لـ Flutter Pulse (2023)، يتم اختيار Floor في 28% من مشاريع Flutter التي تتطلب قاعدة بيانات محلية. الأسباب الرئيسية لاختياره هي الألفة مع SQL (لا حاجة لتعلم لغة استعلام جديدة) والتحقق من الاستعلامات في وقت الترجمة. بالإضافة إلى ذلك، يولد Floor كودًا قابلًا للقراءة يسهل تصحيحه مقارنة بـ ORM الأكثر تجريدًا مع DSL مخصص، مما يخفض حاجز الدخول للمطورين الجدد في الفريق.

هندسة Floor

يتكون Floor من ثلاث طبقات: Entity (نموذج الجدول)، DAO (واجهة الاستعلامات) و Database (نقطة الدخول). يقوم المولد بإنشاء تطبيقات _$_Entity لتعيين الحقول و _$_Dao لتنفيذ SQL. عندما يتغير Entity أو DAO، يكفي إعادة تشغيل build_runner — يتم تحديث الكود تلقائيًا. للترحيل بين إصدارات المخطط، يستخدم Floor أرقام إصدارات متسلسلة، مما يضمن سلامة البيانات عند تحديث التطبيق على أجهزة المستخدمين.

كيف يعمل Floor في Flutter؟

يستخدم Floor SQLite من خلال حزمة sqflite للإصدارات المنصية و sqlite3 لسطح المكتب والويب. عند بدء التطبيق، يقوم Floor بإنشاء أو فتح ملف SQLite، وتطبيق الترحيلات وإعداد طرق DAO لتنفيذ الاستعلامات. يتم تنفيذ جميع العمليات بشكل غير متزامن عبر Future و Stream.

يعمل توليد الكود في Floor على النحو التالي: يقرأ المحلل التعليقات التوضيحية من الملفات المصدر، وينشئ AST (شجرة بناء الجمل المجردة) للنماذج والاستعلامات، ثم يولد ملفات Dart بالبادئة _$. يتضمن الكود المولد مهام تعيين ResultSet → Entity والعكس.

أمان الخيوط

يعمل Floor مع SQLite في isolate واحد. يتم تنفيذ جميع الاستعلامات بشكل غير متزامن، ولكن تتم قفل الكتابات المتزامنة على مستوى SQLite. للمعاملات، يتم استخدام التعليق التوضيحي @transaction الذي يضمن ذرية مجموعة الاستعلامات والتراجع عند الخطأ.

Floor vs Drift: مقارنة ORM لـ Flutter

كل من Floor و Drift هما ORM فوق SQLite، لكنهما يختلفان في الفلسفة. Floor أقرب إلى Room من Android، بينما Drift أكثر تفاعلية مع API Stream مدمج وتجميع الاستعلامات عبر ملفات SQL. يعتمد الاختيار بينهما على خبرة الفريق والتفاعلية المطلوبة.

الخاصيةFloorDrift
نوع الاستعلاماتسلاسل SQL في @Queryطرق Dart + ملفات sql
توليد الكودfloor_generator (build_runner)drift_dev (build_runner)
التفاعليةStream من DAOAPI Stream مدمج + تحديث تلقائي
التعقيدمنخفض (SQL مألوف)متوسط (DSL خاص)
الترحيلنصوص SQL يدويةتلقائي + يدوي
التوافقAndroid، iOS، macOSAndroid، iOS، Web، macOS، Linux

متى تختار Floor

Floor هو الخيار للفرق المألوفة بالفعل مع SQL و Android Room. إذا كان المطورون معتادين على كتابة استعلامات SQL يدويًا ويريدون غلافًا بسيطًا فوق SQLite — فإن Floor يوفر كتابة الأنواع دون تعلم DSL جديد. كما أنه أسهل في التصحيح لأن الكود المولد قابل للقراءة ومتوقع.

متى يكون Drift أفضل

يوفر Drift تفاعلية أكثر قوة ويدعم منصات أكثر. إذا كان التطبيق يستخدم Stream بشكل نشط لتحديث واجهة المستخدم، أو يتطلب استعلامات معقدة مع JOIN والاستعلامات الفرعية، أو يستهدف الويب — فإن Drift أفضل. ومع ذلك، فإن حاجز الدخول أعلى بسبب الحاجة لتعلم DSL الخاص به.

أمثلة كود مع Floor

يتم بناء Floor حول التعليقات التوضيحية. فيما يلي مثال كامل لـ Entity و DAO و Database لتطبيق قائمة المهام. بعد تشغيل build_runner، تكون الفئات المولدة جاهزة للاستخدام.

تعريف Entity و DAO

فئة TaskEntity مع التعليق التوضيحي @Entity يتم تعيينها إلى جدول المهام. حقل مع @primaryKey يصبح المفتاح الأساسي. واجهة TaskDao تحتوي على طرق لعمليات الجدول — كل طريقة مُعلَّمة بـ @Query أو @Insert أو @Update أو @Delete.

dart
@entity
class TaskEntity {
    @PrimaryKey(autoGenerate: true)
    final int id;
    final String title;
    final bool isCompleted;
    final int priority;

    TaskEntity({this.id, required this.title,
        this.isCompleted = false, this.priority = 0});
}

@dao
abstract class TaskDao {
    @Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
    Future<List<TaskEntity>> getAllTasks();

    @Insert
    Future<int> insertTask(TaskEntity task);

    @Update
    Future<void> updateTask(TaskEntity task);

    @Query('SELECT * FROM TaskEntity WHERE isCompleted = :status')
    Stream<List<TaskEntity>> watchTasks(bool status);
}

تهيئة قاعدة البيانات

فئة مجردة مع التعليق التوضيحي @Database تربط Entity و DAO. طريقة databaseBuilder تنشئ مثيل قاعدة البيانات. بعد استدعاء build، تكون قاعدة البيانات جاهزة: يفتح Floor ملف SQLite، ويطبق الترحيلات ويعيد DAO للعمل.

dart
@Database(version: 1, entities: [TaskEntity])
abstract class AppDatabase extends FloorDatabase {
    TaskDao get taskDao;
}

// الاستخدام
final database = await $FloorAppDatabase.databaseBuilder('app.db').build();
final taskDao = database.taskDao;
final tasks = await taskDao.getAllTasks();

استعلامات تفاعلية عبر Stream

يدعم Floor إرجاع Stream من طرق DAO. عند أي تغييرات في الجدول، يطلق Stream قائمة جديدة. يتكامل هذا مع StreamBuilder في Flutter — يتم تحديث واجهة المستخدم تلقائيًا عند إضافة أو تعديل أو حذف السجلات.

dart
@Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
Stream<List<TaskEntity>> watchAllTasks();

// في Flutter widget
StreamBuilder<List<TaskEntity>>(
    stream: taskDao.watchAllTasks(),
    builder: (context, snapshot) {
        final tasks = snapshot.data ?? [];
        return ListView.builder(
            itemCount: tasks.length,
            itemBuilder: (_, i) => TaskTile(tasks[i]),
        );
    },
)

الترحيل وإصدار الإصدارات في Floor

يدعم Floor إصدار قاعدة البيانات عبر معامل version في التعليق التوضيحي @Database. عندما يتغير Entity (إضافة أو إزالة حقول)، تحتاج إلى زيادة الإصدار وإضافة ترحيل. الترحيل هو دالة Dart تستقبل معاملة وتنفذ استعلامات SQL ALTER TABLE.

مثال ترحيل

لنفترض أنه في الإصدار 2 أضفنا حقل dueDate إلى TaskEntity. يتم تنفيذ الترحيل عبر استعلام SQL ALTER TABLE. إذا لم يتم تحديد ترحيل، يستدعي Floor MigrationStrategy حيث يمكنك تعيين fallback (على سبيل المثال، إعادة إنشاء الجدول مع فقدان البيانات).

اختبار استعلامات Floor

لا يوفر Floor إطار عمل mock مدمج، ولكن يمكن استبدال قاعدة البيانات بسهولة في الاختبارات. أنشئ inMemoryDatabaseBuilder — ينشئ قاعدة بيانات SQLite في الذاكرة مطابقة في المخطط للإنتاج. بعد كل اختبار، امسح البيانات عبر deleteDatabase لعزل سيناريوهات الاختبار.

المعاملات والعمليات المجمعة في Floor

يدعم Floor المعاملات عبر التعليق التوضيحي @transaction على طرق DAO. داخل المعاملة، يتم تنفيذ استعلامات متعددة بشكل متسلسل مع ضمان التراجع عند الخطأ. الإدراج المجمع عبر @Insert مع معامل List يحسن إدراج سجلات متعددة في استدعاء واحد — وهذا أسرع بعدة مرات من إدراج سجل واحد في كل مرة في حلقة. للعمليات الكبيرة، استخدم الإدراج المجمع من 100–200 سجل: هذا هو التوازن الأمثل بين سرعة التنفيذ واستهلاك RAM على الأجهزة المحمولة ذات الموارد المحدودة.

dart
final migration1to2 = Migration(1, 2, (database) async {
    await database.execute(
        'ALTER TABLE TaskEntity ADD COLUMN dueDate TEXT'
    );
});

final database = await $FloorAppDatabase.databaseBuilder('app.db')
    .addMigrations([migration1to2])
    .build();

الأسئلة الشائعة

كيف يختلف Floor عن sqflite الخام؟

sqflite يتطلب كتابة استعلامات SQL يدويًا وتعيين ResultSet إلى كائنات. Floor يولد هذا الكود تلقائيًا: تصف Entity و DAO، والطرق المكتوبة تعيد كائنات Dart جاهزة. Floor أيضًا يتحقق من استعلامات SQL في وقت الترجمة عبر التعليقات التوضيحية.

هل يدعم Floor العلاقات بين الجداول؟

Floor ليس لديه تعليقات توضيحية مدمجة للعلاقات (ForeignKey، @Relation) مثل Room. يتم تنفيذ العلاقات عبر استعلامات SQL JOIN يدوية في @Query. للمخططات العلائقية المعقدة، فكر في Drift مع دعمه المدمج للعلاقات.

كيف يتم تصحيح استعلامات SQL في Floor؟

يسمح Floor بتمكين callback عند إنشاء DatabaseBuilder — يتم تمرير مثيل sqflite.Database إليه يمكنك ربط مسجل به. بدلاً من ذلك، استخدم floor_doctor لتصور المخطط والبيانات في وضع التطوير.

هل يمكن استخدام Floor لإصدارات الويب؟

Floor يستخدم sqflite الذي لا يعمل في بيئة الويب. للويب، يلزم إصدار منفصل مع sqlite3 عبر WASM. في الإصدار الحالي، يدعم Floor رسميًا Android و iOS و macOS. للويب، استخدم Drift مع محول sqlite3.

كيف يعمل تخزين الاستعلامات المؤقت في Floor؟

Floor ليس لديه تخزين مؤقت مدمج — كل استعلام يتم تنفيذه على SQLite. لتخزين الاستعلامات المتكررة مؤقتًا، استخدم طبقة Repository مع ذاكرة تخزين مؤقت في الذاكرة (على سبيل المثال، dart_cache). Floor فقط يولد كودًا للعمل مع SQLite دون إضافة أي تحميل زائد فوقه.

الخلاصة

  • Floor — ORM فوق SQLite مع توليد كود Entity و DAO و Database عبر التعليقات التوضيحية
  • أمان الأنواع — استعلامات SQL تُفحص في وقت الترجمة عبر التعليق التوضيحي @Query
  • نمط DAO — استعلامات SQL مُغلَّفة في طرق Dart، تفصل النموذج عن المنطق
  • الترحيل — إصدار المخطط عبر Migration مع ALTER TABLE يدوي
  • التفاعلية — Stream من DAO لتحديث واجهة المستخدم تلقائيًا عند التغييرات
  • القيود — لا توجد علاقات مدمجة، لا يدعم إصدارات الويب
  • توصية — اختر Floor لمشاريع Flutter حيث يكون الفريق على دراية بـ SQL ونهج Room

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا