Floor — ORM (Object-Relational Mapping) за Flutter, који пружа типизирани слој изнад SQLite. За разлику од сирових SQLite упита, Floor генерише DAO класе из анотираних Dart модела. Према подацима Pub.dev, 2024, Floor се користи у преко 3500 Flutter пројеката и налази се у прва три најпопуларнија ORM решења за локално складиштење података, поред drift и hive.
Главни закључци
Floor — је ORM библиотека за Flutter и Dart, изграђена на бази SQLite. Користи анотације за описивање ентитета (Entity), Data Access Object (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 се састоји од три слоја: Entity (модел табеле), DAO (интерфејс упита) и Database (улазна тачка). Генератор креира имплементације _$_Entity за мапирање поља и _$_Dao за извршавање SQL-а. При промени Entity или DAO довољно је поново покренути build_runner — код ће се ажурирати аутоматски. За миграцију између верзија шеме, Floor користи секвенцијалне бројеве верзија, што гарантује интегритет података при ажурирању апликације на уређајима корисника.
Floor користи SQLite преко пакета sqflite за платформске компилације и sqlite3 за десктоп и веб. При покретању апликације, Floor креира или отвара SQLite датотеку, примењује миграције и припрема DAO методе за извршавање упита. Све операције се извршавају у асинхроном режиму преко Future и Stream.
Генерисање кода у Floor-у ради по следећем принципу: парсер чита анотације из изворног кода, креира AST (Abstract Syntax Tree) модела и упита, затим генерише Dart датотеке са префиксом _$. Генерисани код укључује мапере ResultSet → Entity и обрнуто.
Floor ради са SQLite у једном изолату. Сви упити се извршавају асинхроно, али конкурентни уписи се блокирају на нивоу SQLite. За трансакције се користи анотација @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 је изграђен око анотација. Испод је потпун пример Entity, DAO и Database за апликацију листе задатака. Након покретања build_runner, генерисане класе су спремне за употребу.
Класа TaskEntity са анотацијом @Entity мапира се на табелу task. Поље са @primaryKey постаје примарни кључ. Интерфејс TaskDao садржи методе за операције са табелом — свака метода је анотирана са @Query, @Insert, @Update или @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);
}
Апстрактна класа са анотацијом @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 у анотацији @Database. При промени Entity (додавању или уклањању поља) потребно је повећати верзију и додати миграцију. Миграција је Dart функција која прима трансакцију и извршава SQL упите ALTER TABLE.
Претпоставимо да смо у верзији 2 додали поље dueDate у TaskEntity. Миграција се извршава SQL упитом ALTER TABLE. Ако миграција није наведена, Floor позива MigrationStrategy, где се може поставити fallback (нпр. поновно креирање табеле са губитком података).
Floor не пружа уграђени mock оквир, али база података се лако може заменити у тестовима. Креирајте inMemoryDatabaseBuilder — он креира SQLite базу у меморији, идентичну по шеми продукционој. Након сваког теста, очистите податке преко deleteDatabase за изолацију тест сценарија.
Floor подржава трансакције кроз анотацију @transaction на DAO методи. Унутар трансакције, више упита се извршава секвенцијално са гаранцијом повлачења при грешци. Пакетно уметање преко @Insert са параметром List<T> оптимизује уметање више записа у једном позиву — ово је неколико пута брже од уметања појединачних записа у петљи. За масовне операције користите batch уметање од 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 упите у фази компилације кроз анотације.
Floor нема уграђене анотације за везе (ForeignKey, @Relation), као Room. Везе се имплементирају кроз ручне SQL JOIN упите у @Query. За сложене релационе шеме боље је размотрити Drift са његовом уграђеном подршком за релације.
Floor омогућава укључивање callback-а callback при креирању DatabaseBuilder — у њега се прослеђује инстанца sqflite.Database на коју се може закачити логер. Алтернативно, користите floor_doctor за визуализацију шеме и података у dev режиму.
Floor користи sqflite, који не ради у веб окружењу. За веб је потребна посебна компилација са sqlite3 преко WASM-а. У тренутној верзији, Floor званично подржава Android, iOS и macOS. За веб користите Drift са sqlite3 адаптером.
Floor нема уграђено кеширање — сваки упит се извршава ка SQLite-у. За кеширање понављајућих упита користите Repository слој са in-memory кешом (нпр. dart_cache). Floor само генерише код за рад са SQLite-ом, не додајући надградње изнад њега.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође