Floor — ORM (Object-Relational Mapping) برای Flutter است که یک لایه تایپشده روی SQLite ارائه میدهد. برخلاف پرسوجوهای خام SQLite، Floor کلاسهای DAO را از مدلهای Dart حاشیهنویسی شده تولید میکند. بر اساس دادههای Pub.dev، 2024، Floor در بیش از 3500 پروژه Flutter استفاده میشود و در کنار drift و hive جزو سه راهحل محبوب ORM برای ذخیرهسازی محلی دادهها است.
نکات کلیدی
Floor — یک کتابخانه ORM برای Flutter و Dart است که بر اساس SQLite ساخته شده است. از annotationها برای توصیف موجودیتها (Entity)، Data Access Object (DAO) و پایگاه داده (Database) استفاده میکند. تولید کد از طریق build_runner و floor_generator انجام میشود — کامپایلر پیادهسازیهای DAO و کلاس مدیریت پایگاه داده را ایجاد میکند. برخلاف sqflite خام، Floor نیاز به تبدیل دستی ResultSet به اشیاء Dart را کاملاً از بین میبرد و به طور خودکار ستونها را به فیلدهای Entity از طریق بازتاب نوع نگاشت میکند.
Floor از الگوی Repository + DAO پیروی میکند که برای توسعهدهندگان Android از Room آشنا است. هر جدول با یک کلاس Dart با annotation @Entity نمایش داده میشود، پرسوجوهای SQL در رابطهای دارای annotation @dao گروهبندی میشوند و پایگاه داده در یک کلاس انتزاعی با @Database جمعآوری میشود. این رویکرد مدل داده و منطق پرسوجو را به طور دقیق جدا میکند.
بر اساس Flutter Pulse (2023)، Floor در 28% از پروژههای Flutter که نیاز به پایگاه داده محلی دارند انتخاب میشود. دلایل اصلی انتخاب — آشنایی با SQL (نیازی به یادگیری زبان پرسوجوی جدید نیست) و بررسی پرسوجوها در مرحله کامپایل است. علاوه بر این، Floor کد قابل خواندنی تولید میکند که برخلاف ORMهای انتزاعیتر با DSL سفارشی، به راحتی اشکالزدایی میشود و آستانه ورود را برای توسعهدهندگان جدید تیم کاهش میدهد.
Floor از سه لایه تشکیل شده است: Entity (مدل جدول)، DAO (رابط پرسوجو) و Database (نقطه ورود). مولد پیادهسازیهای _$_Entity برای نگاشت فیلدها و _$_Dao برای اجرای SQL ایجاد میکند. هنگام تغییر Entity یا DAO کافی است build_runner را مجدداً اجرا کنید — کد به طور خودکار بهروزرسانی میشود. برای مهاجرت بین نسخههای طرح، Floor از شماره نسخههای متوالی استفاده میکند که یکپارچگی دادهها را در هنگام بهروزرسانی برنامه در دستگاههای کاربران تضمین میکند.
Floor از SQLite از طریق بسته sqflite برای بیلدهای پلتفرمی و sqlite3 برای دسکتاپ و وب استفاده میکند. هنگام راهاندازی برنامه، Floor فایل SQLite را ایجاد یا باز میکند، مهاجرتها را اعمال میکند و متدهای DAO را برای اجرای پرسوجوها آماده میسازد. تمام عملیات در حالت ناهمگام از طریق Future و Stream انجام میشوند.
تولید کد در Floor بر اساس اصل زیر کار میکند: تجزیهگر annotationها را از کدهای منبع میخواند، AST (درخت نحو انتزاعی) مدلها و پرسوجوها را ایجاد میکند، سپس فایلهای Dart با پیشوند _$ تولید میکند. کد تولید شده شامل نگاشتگرهای ResultSet → Entity و برعکس است.
Floor با SQLite در یک ایزوله کار میکند. تمام پرسوجوها به صورت ناهمگام اجرا میشوند، اما نوشتنهای همزمان در سطح SQLite مسدود میشوند. برای تراکنشها از annotation @transaction استفاده میشود که اتمی بودن گروه پرسوجوها و بازگشت در صورت خطا را تضمین میکند.
هم Floor و هم Drift — ORM روی SQLite هستند، اما در فلسفه تفاوت دارند. Floor به Room در Android نزدیکتر است، Drift — واکنشگراتر با Stream API داخلی و کامپایل پرسوجو از طریق فایلهای SQL. انتخاب بین آنها به تجربه تیم و واکنشگرایی مورد نیاز بستگی دارد.
| ویژگی | Floor | Drift |
|---|---|---|
| نوع پرسوجو | رشتههای SQL در @Query | متدهای Dart + فایلهای sql |
| تولید کد | floor_generator (build_runner) | drift_dev (build_runner) |
| واکنشگرایی | Stream از DAO | Stream API داخلی + بهروزرسانی خودکار |
| پیچیدگی | کم (SQL آشنا) | متوسط (DSL خاص خود) |
| مهاجرتها | اسکریپتهای دستی SQL | خودکار + دستی |
| سازگاری | Android، iOS، macOS | Android، iOS، Web، macOS، Linux |
Floor — انتخاب تیمهایی است که قبلاً با SQL و Android Room آشنا هستند. اگر توسعهدهندگان عادت دارند پرسوجوهای SQL را دستی بنویسند و یک پوشش حداقلی روی SQLite میخواهند — Floor بدون یادگیری DSL جدید، تایپسازی ارائه میدهد. همچنین اشکالزدایی آن آسانتر است، زیرا کد تولید شده قابل خواندن و قابل پیشبینی است.
Drift واکنشگرایی قدرتمندتری ارائه میدهد و پلتفرمهای بیشتری را پشتیبانی میکند. اگر برنامه به طور فعال از Stream برای بهروزرسانی UI استفاده میکند، نیاز به پرسوجوهای پیچیده با JOIN و زیرپرسوجو دارد یا برای وب ساخته میشود — Drift ارجح است. با این حال، آستانه ورود آن به دلیل نیاز به یادگیری DSL خاص خود بالاتر است.
Floor حول annotationها ساخته شده است. در زیر یک مثال کامل از Entity، DAO و Database برای یک برنامه لیست وظایف آورده شده است. پس از اجرای build_runner، کلاسهای تولید شده آماده استفاده هستند.
کلاس TaskEntity با annotation @Entity به جدول task نگاشت میشود. فیلد با @primaryKey به کلید اصلی تبدیل میشود. رابط TaskDao شامل متدهایی برای عملیات روی جدول است — هر متد با @Query، @Insert، @Update یا @Delete annotation شده است.
@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);
}
کلاس انتزاعی با annotation @Database Entity و DAO را به هم متصل میکند. متد databaseBuilder یک نمونه از پایگاه داده ایجاد میکند. پس از فراخوانی build، پایگاه داده آماده است: Floor فایل SQLite را باز میکند، مهاجرتها را اعمال میکند و DAO را برای کار برمیگرداند.
@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();
Floor از بازگرداندن Stream از متدهای DAO پشتیبانی میکند. با هر تغییری در جدول، Stream یک لیست جدید منتشر میکند. این با StreamBuilder در Flutter یکپارچه میشود — UI با اضافه شدن، تغییر یا حذف رکوردها به طور خودکار بهروزرسانی میشود.
@Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
Stream<List<TaskEntity>> watchAllTasks();
// در ویجت Flutter
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 از نسخهبندی پایگاه داده از طریق پارامتر version در annotation @Database پشتیبانی میکند. هنگام تغییر Entity (اضافه یا حذف فیلدها) باید نسخه را افزایش داده و یک مهاجرت اضافه کنید. مهاجرت — یک تابع Dart است که یک تراکنش دریافت میکند و پرسوجوهای SQL ALTER TABLE را اجرا میکند.
فرض کنید در نسخه 2 فیلد dueDate را به TaskEntity اضافه کردیم. مهاجرت با پرسوجوی SQL ALTER TABLE اجرا میشود. اگر مهاجرت مشخص نشده باشد، Floor MigrationStrategy را فراخوانی میکند، جایی که میتوان fallback تنظیم کرد (مثلاً بازسازی جدول با از دست دادن داده).
Floor چارچوب داخلی mock ارائه نمیدهد، اما پایگاه داده را میتوان به راحتی در تستها جایگزین کرد. inMemoryDatabaseBuilder ایجاد کنید — یک پایگاه داده SQLite در حافظه میسازد که از نظر طرح با تولید یکسان است. پس از هر تست، دادهها را از طریق deleteDatabase برای ایزولهسازی سناریوهای تست پاک کنید.
Floor از تراکنشها از طریق annotation @transaction روی متد DAO پشتیبانی میکند. در داخل تراکنش، چندین پرسوجو به صورت متوالی با تضمین بازگشت در صورت خطا اجرا میشوند. درج دستهای از طریق @Insert با پارامتر List<T> درج چندین رکورد را در یک فراخوانی بهینه میکند — این چندین برابر سریعتر از درج تکتک رکوردها در یک حلقه است. برای عملیات انبوه از درج دستهای 100–200 رکورد استفاده کنید: این تعادل بهینه بین سرعت اجرا و مصرف RAM در دستگاههای همراه با منابع محدود است.
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();
سوالات متداول
sqflite نیاز به نوشتن دستی پرسوجوهای SQL و نگاشت ResultSet به اشیاء دارد. Floor این کد را به طور خودکار تولید میکند: شما Entity و DAO را توصیف میکنید و متدهای تایپشده اشیاء Dart آماده را برمیگردانند. Floor همچنین پرسوجوهای SQL را در مرحله کامپایل از طریق annotationها بررسی میکند.
Floor annotationهای داخلی برای روابط (ForeignKey، @Relation) مانند Room ندارد. روابط از طریق پرسوجوهای دستی SQL JOIN در @Query پیادهسازی میشوند. برای طرحهای رابطهای پیچیده بهتر است Drift را با پشتیبانی داخلی از روابط در نظر بگیرید.
Floor امکان فعالسازی callback callback را هنگام ایجاد DatabaseBuilder فراهم میکند — نمونه sqflite.Database به آن ارسال میشود که میتوان یک logger به آن متصل کرد. جایگزین، از floor_doctor برای تجسم طرح و دادهها در حالت توسعه استفاده کنید.
Floor از sqflite استفاده میکند که در محیط وب کار نمیکند. برای وب به یک بیلد جداگانه با sqlite3 از طریق WASM نیاز است. در نسخه فعلی، Floor به طور رسمی از Android، iOS و macOS پشتیبانی میکند. برای وب از Drift با آداپتور sqlite3 استفاده کنید.
Floor کش داخلی ندارد — هر پرسوجو به SQLite اجرا میشود. برای کش کردن پرسوجوهای تکراری از لایه Repository با کش درون حافظه استفاده کنید (مثلاً dart_cache). Floor فقط کد کار با SQLite را تولید میکند بدون اینکه لایههای اضافی روی آن اضافه کند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید