Floor — ORM (Object-Relational Mapping) cho Flutter, cung cấp một lớp được đánh kiểu trên SQLite. Không giống như các truy vấn SQLite thô, Floor tạo ra các lớp DAO từ các mô hình Dart được chú thích. Theo Pub.dev, 2024, Floor được sử dụng trong hơn 3500 dự án Flutter và nằm trong top ba giải pháp ORM phổ biến nhất cho lưu trữ dữ liệu cục bộ cùng với drift và hive.
Điểm Chính
Floor là một thư viện ORM cho Flutter và Dart được xây dựng trên SQLite. Nó sử dụng annotation để mô tả các thực thể (Entity), Data Access Objects (DAO) và cơ sở dữ liệu (Database). Việc tạo mã được thực hiện thông qua build_runner và floor_generator — trình biên dịch tạo ra các triển khai DAO và một lớp quản lý cơ sở dữ liệu. Không giống như sqflite thô, Floor loại bỏ hoàn toàn nhu cầu chuyển đổi thủ công từ ResultSet sang đối tượng Dart bằng cách tự động ánh xạ các cột sang trường Entity thông qua phản xạ kiểu.
Floor tuân theo mẫu Repository + DAO quen thuộc với các nhà phát triển Android từ Room. Mỗi bảng được đại diện bởi một lớp Dart với annotation @Entity, các truy vấn SQL được nhóm trong các giao diện với annotation @dao, và cơ sở dữ liệu được tổ hợp trong một lớp trừu tượng với @Database. Cách tiếp cận này phân tách chặt chẽ mô hình dữ liệu khỏi logic truy vấn.
Theo Flutter Pulse (2023), Floor được chọn trong 28% các dự án Flutter yêu cầu cơ sở dữ liệu cục bộ. Lý do chính để chọn nó là quen thuộc với SQL (không cần học ngôn ngữ truy vấn mới) và kiểm tra truy vấn tại thời điểm biên dịch. Ngoài ra, Floor tạo ra mã có thể đọc được, dễ dàng gỡ lỗi so với các ORM trừu tượng hơn với DSL tùy chỉnh, giúp giảm rào cản gia nhập cho các nhà phát triển mới trong nhóm.
Floor bao gồm ba lớp: Entity (mô hình bảng), DAO (giao diện truy vấn) và Database (điểm vào). Trình tạo sẽ tạo ra các triển khai _$_Entity cho ánh xạ trường và _$_Dao để thực thi SQL. Khi Entity hoặc DAO thay đổi, chỉ cần khởi động lại build_runner — mã sẽ tự động cập nhật. Để di chuyển giữa các phiên bản schema, Floor sử dụng số phiên bản tuần tự, đảm bảo tính toàn vẹn dữ liệu khi cập nhật ứng dụng trên thiết bị người dùng.
Floor sử dụng SQLite thông qua gói sqflite cho các bản dựng nền tảng và sqlite3 cho máy tính để bàn và web. Khi ứng dụng khởi động, Floor tạo hoặc mở tệp SQLite, áp dụng các di chuyển và chuẩn bị các phương thức DAO để thực thi truy vấn. Tất cả các thao tác được thực hiện bất đồng bộ thông qua Future và Stream.
Việc tạo mã trong Floor hoạt động như sau: trình phân tích đọc các annotation từ tệp nguồn, tạo AST (Cây cú pháp trừu tượng) của các mô hình và truy vấn, sau đó tạo các tệp Dart với tiền tố _$. Mã được tạo bao gồm các trình ánh xạ ResultSet → Entity và ngược lại.
Floor làm việc với SQLite trong một isolate duy nhất. Tất cả các truy vấn được thực thi bất đồng bộ, nhưng các ghi đồng thời bị khóa ở cấp độ SQLite. Đối với các giao dịch, annotation @transaction được sử dụng, đảm bảo tính nguyên tử của một nhóm truy vấn và rollback khi có lỗi.
Cả Floor và Drift đều là ORM trên SQLite, nhưng chúng khác nhau về triết lý. Floor gần với Room của Android hơn, Drift phản ứng hơn với API Stream tích hợp và biên dịch truy vấn thông qua tệp SQL. Lựa chọn giữa chúng phụ thuộc vào kinh nghiệm của nhóm và tính phản ứng cần thiết.
| Đặc điểm | Floor | Drift |
|---|---|---|
| Loại truy vấn | Chuỗi SQL trong @Query | Phương thức Dart + tệp sql |
| Tạo mã | floor_generator (build_runner) | drift_dev (build_runner) |
| Tính phản ứng | Stream từ DAO | API Stream tích hợp + tự động cập nhật |
| Độ phức tạp | Thấp (SQL quen thuộc) | Trung bình (DSL riêng) |
| Di chuyển | Tập lệnh SQL thủ công | Tự động + thủ công |
| Tương thích | Android, iOS, macOS | Android, iOS, Web, macOS, Linux |
Floor là lựa chọn cho các nhóm đã quen thuộc với SQL và Android Room. Nếu các nhà phát triển đã quen viết truy vấn SQL thủ công và muốn một lớp bọc tối thiểu trên SQLite — Floor cung cấp khả năng đánh kiểu mà không cần học DSL mới. Nó cũng dễ gỡ lỗi hơn vì mã được tạo có thể đọc được và có thể dự đoán trước.
Drift cung cấp tính phản ứng mạnh mẽ hơn và hỗ trợ nhiều nền tảng hơn. Nếu ứng dụng sử dụng Stream tích cực để cập nhật UI, yêu cầu các truy vấn phức tạp với JOIN và truy vấn con hoặc nhắm mục tiêu web — Drift là lựa chọn tốt hơn. Tuy nhiên, rào cản gia nhập cao hơn do cần học DSL riêng của nó.
Floor được xây dựng xung quanh các annotation. Dưới đây là một ví dụ đầy đủ về Entity, DAO và Database cho một ứng dụng danh sách công việc. Sau khi chạy build_runner, các lớp được tạo sẵn sàng để sử dụng.
Lớp TaskEntity với annotation @Entity ánh xạ tới bảng task. Trường có @primaryKey trở thành khóa chính. Giao diện TaskDao chứa các phương thức cho các thao tác với bảng — mỗi phương thức được chú thích với @Query, @Insert, @Update hoặc @Delete.
@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);
}
Một lớp trừu tượng với annotation @Database liên kết Entity và DAO. Phương thức databaseBuilder tạo một thể hiện cơ sở dữ liệu. Sau khi gọi build, cơ sở dữ liệu đã sẵn sàng: Floor mở tệp SQLite, áp dụng các di chuyển và trả về DAO để làm việc.
@Database(version: 1, entities: [TaskEntity])
abstract class AppDatabase extends FloorDatabase {
TaskDao get taskDao;
}
// Sử dụng
final database = await $FloorAppDatabase.databaseBuilder('app.db').build();
final taskDao = database.taskDao;
final tasks = await taskDao.getAllTasks();
Floor hỗ trợ trả về Stream từ các phương thức DAO. Khi có bất kỳ thay đổi nào trong bảng, Stream sẽ phát ra một danh sách mới. Điều này tích hợp với StreamBuilder trong Flutter — UI tự động cập nhật khi các bản ghi được thêm, sửa hoặc xóa.
@Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
Stream<List<TaskEntity>> watchAllTasks();
// Trong 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 hỗ trợ quản lý phiên bản cơ sở dữ liệu thông qua tham số version trong annotation @Database. Khi Entity thay đổi (thêm hoặc xóa trường), bạn cần tăng phiên bản và thêm một di chuyển. Di chuyển là một hàm Dart nhận một giao dịch và thực thi các truy vấn SQL ALTER TABLE.
Giả sử ở phiên bản 2, chúng tôi đã thêm trường dueDate vào TaskEntity. Di chuyển được thực hiện thông qua truy vấn SQL ALTER TABLE. Nếu không chỉ định di chuyển, Floor gọi MigrationStrategy nơi bạn có thể đặt fallback (ví dụ: tạo lại bảng với mất dữ liệu).
Floor không cung cấp framework mẫu tích hợp, nhưng cơ sở dữ liệu có thể dễ dàng được thay thế trong các bài kiểm tra. Tạo inMemoryDatabaseBuilder — nó tạo một cơ sở dữ liệu SQLite trong bộ nhớ có schema giống hệt sản xuất. Sau mỗi bài kiểm tra, hãy xóa dữ liệu qua deleteDatabase để cô lập các kịch bản kiểm tra.
Floor hỗ trợ các giao dịch thông qua annotation @transaction trên các phương thức DAO. Bên trong một giao dịch, nhiều truy vấn được thực thi tuần tự với bảo đảm rollback khi có lỗi. Chèn hàng loạt thông qua @Insert với tham số List
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();
Câu Hỏi Thường Gặp
sqflite yêu cầu viết thủ công các truy vấn SQL và ánh xạ ResultSet sang đối tượng. Floor tạo mã này tự động: bạn mô tả Entity và DAO, và các phương thức được đánh kiểu trả về các đối tượng Dart sẵn sàng sử dụng. Floor cũng kiểm tra các truy vấn SQL tại thời điểm biên dịch thông qua annotation.
Floor không có annotation tích hợp cho các mối quan hệ (ForeignKey, @Relation) như Room. Các mối quan hệ được thực hiện thông qua các truy vấn SQL JOIN thủ công trong @Query. Đối với các lược đồ quan hệ phức tạp, hãy cân nhắc Drift với hỗ trợ mối quan hệ tích hợp của nó.
Floor cho phép bật callback khi tạo DatabaseBuilder — nó nhận một thể hiện của sqflite.Database mà bạn có thể gắn logger. Thay vào đó, hãy sử dụng floor_doctor để trực quan hóa schema và dữ liệu ở chế độ dev.
Floor sử dụng sqflite, không hoạt động trong môi trường web. Đối với web, cần một bản dựng riêng với sqlite3 qua WASM. Trong phiên bản hiện tại, Floor chính thức hỗ trợ Android, iOS và macOS. Đối với web, hãy sử dụng Drift với bộ chuyển đổi sqlite3.
Floor không có bộ nhớ đệm tích hợp — mỗi truy vấn được thực thi trên SQLite. Để lưu vào bộ nhớ đệm các truy vấn lặp lại, hãy sử dụng lớp Repository với bộ nhớ đệm trong bộ nhớ (ví dụ: dart_cache). Floor chỉ tạo mã để làm việc với SQLite mà không thêm chi phí trên nó.
Tổng Kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm