Floor — ORM (Object-Relational Mapping) pro Flutter, poskytující typovanou vrstvu nad SQLite. Na rozdíl od raw SQLite dotazů Floor generuje DAO třídy z anotovaných Dart modelů. Podle údajů Pub.dev, 2024 je Floor používán ve více než 3500 Flutter projektech a patří mezi tři nejoblíbenější ORM řešení pro lokální ukládání dat vedle drift a hive.
Hlavní body
Floor — je ORM knihovna pro Flutter a Dart, postavená na SQLite. Používá anotace k popisu entit (Entity), Data Access Object (DAO) a databáze (Database). Generování kódu se provádí prostřednictvím build_runner a floor_generator — kompilátor vytváří implementace DAO a řídící třídu databáze. Na rozdíl od raw sqflite Floor zcela odstraňuje potřebu ručního převodu ResultSet na Dart objekty, automaticky mapuje sloupce na pole Entity pomocí reflexe typů.
Floor následuje vzor Repository + DAO, známý vývojářům Android z Room. Každá tabulka je reprezentována Dart třídou s anotací @Entity, SQL dotazy jsou seskupeny v rozhraních s anotací @dao a databáze je sestavena v abstraktní třídě s @Database. Tento přístup striktně odděluje datový model a logiku dotazů.
Podle Flutter Pulse (2023) je Floor vybrán ve 28% Flutter projektů, které vyžadují lokální databázi. Hlavní důvody výběru — znalost SQL (není třeba učit se nový dotazovací jazyk) a kontrola dotazů při kompilaci. Kromě toho Floor generuje čitelný kód, který se snadno debuguje na rozdíl od abstraktnějších ORM s vlastním DSL, což snižuje práh vstupu pro nové vývojáře v týmu.
Floor se skládá ze tří vrstev: Entity (model tabulky), DAO (rozhraní dotazů) a Database (vstupní bod). Generátor vytváří implementace _$_Entity pro mapování polí a _$_Dao pro provádění SQL. Při změně Entity nebo DAO stačí restartovat build_runner — kód se automaticky aktualizuje. Pro migraci mezi verzemi schématu Floor používá sekvenční čísla verzí, což zaručuje integritu dat při aktualizaci aplikace na zařízeních uživatelů.
Floor používá SQLite prostřednictvím balíčku sqflite pro platformní sestavení a sqlite3 pro desktop a web. Při spuštění aplikace Floor vytvoří nebo otevře SQLite soubor, aplikuje migrace a připraví DAO metody pro provádění dotazů. Všechny operace se provádějí asynchronně prostřednictvím Future a Stream.
Generování kódu ve Floor funguje na následujícím principu: parser čte anotace ze zdrojového kódu, vytváří AST (Abstract Syntax Tree) modelů a dotazů, poté generuje Dart soubory s prefixem _$. Generovaný kód zahrnuje mapování ResultSet → Entity a zpět.
Floor pracuje s SQLite v jednom izolátu. Všechny dotazy se provádějí asynchronně, ale souběžné zápisy jsou blokovány na úrovni SQLite. Pro transakce se používá anotace @transaction, která zaručuje atomicitu skupiny dotazů a vrácení zpět při chybě.
Jak Floor, tak Drift — jsou ORM nad SQLite, ale liší se filozofií. Floor je bližší Room z Androidu, Drift — je reaktivnější s vestavěným Stream API a kompilací dotazů přes SQL soubory. Volba mezi nimi závisí na zkušenostech týmu a požadované reaktivitě.
| Vlastnost | Floor | Drift |
|---|---|---|
| Typ dotazů | SQL řetězce v @Query | Dart metody + sql soubory |
| Generování kódu | floor_generator (build_runner) | drift_dev (build_runner) |
| Reaktivita | Stream z DAO | Vestavěné Stream API + auto-updating |
| Složitost | Nízká (známé SQL) | Střední (vlastní DSL) |
| Migrace | Ruční SQL skripty | Automatické + ruční |
| Kompatibilita | Android, iOS, macOS | Android, iOS, Web, macOS, Linux |
Floor — volba týmů, které již znají SQL a Android Room. Pokud jsou vývojáři zvyklí psát SQL dotazy ručně a chtějí minimální obal nad SQLite — Floor poskytuje typování bez učení nového DSL. Je také snadnější na ladění, protože generovaný kód je čitelný a předvídatelný.
Drift poskytuje silnější reaktivitu a podporuje více platforem. Pokud aplikace aktivně používá Stream pro aktualizaci UI, vyžaduje složité dotazy s JOIN a poddotazy nebo je sestavována pro web — Drift je preferovanější. Jeho práh vstupu je však vyšší kvůli nutnosti učení vlastního DSL.
Floor je postaven kolem anotací. Níže je uveden úplný příklad Entity, DAO a Database pro aplikaci seznamu úkolů. Po spuštění build_runner jsou generované třídy připraveny k použití.
Třída TaskEntity s anotací @Entity se mapuje na tabulku task. Pole s @primaryKey se stává primárním klíčem. Rozhraní TaskDao obsahuje metody pro operace s tabulkou — každá metoda je anotována @Query, @Insert, @Update nebo @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);
}
Abstraktní třída s anotací @Database spojuje Entity a DAO. Metoda databaseBuilder vytváří instanci databáze. Po zavolání build je databáze připravena: Floor otevře SQLite soubor, aplikuje migrace a vrátí DAO pro práci.
@Database(version: 1, entities: [TaskEntity])
abstract class AppDatabase extends FloorDatabase {
TaskDao get taskDao;
}
// Použití
final database = await $FloorAppDatabase.databaseBuilder('app.db').build();
final taskDao = database.taskDao;
final tasks = await taskDao.getAllTasks();
Floor podporuje vracení Stream z DAO metod. Při jakýchkoli změnách v tabulce Stream vydá nový seznam. To se integruje s StreamBuilder ve Flutter — UI se automaticky aktualizuje při přidávání, změně nebo mazání záznamů.
@Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
Stream<List<TaskEntity>> watchAllTasks();
// Ve Flutter widgetu
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 podporuje verzování databáze prostřednictvím parametru version v anotaci @Database. Při změně Entity (přidání nebo odebrání polí) je třeba zvýšit verzi a přidat migraci. Migrace je Dart funkce, která obdrží transakci a provádí SQL dotazy ALTER TABLE.
Řekněme, že ve verzi 2 jsme přidali pole dueDate do TaskEntity. Migrace se provádí SQL dotazem ALTER TABLE. Pokud migrace není uvedena, Floor zavolá MigrationStrategy, kde lze nastavit fallback (například přetvoření tabulky se ztrátou dat).
Floor neposkytuje vestavěný mock framework, ale databázi lze snadno nahradit v testech. Vytvořte inMemoryDatabaseBuilder — vytvoří SQLite databázi v paměti, identickou schématem s produkční. Po každém testu vyčistěte data pomocí deleteDatabase pro izolaci testovacích scénářů.
Floor podporuje transakce prostřednictvím anotace @transaction na DAO metodě. Uvnitř transakce se provádí několik dotazů sekvenčně s garancí vrácení zpět při chybě. Dávkové vkládání přes @Insert s parametrem List<T> optimalizuje vkládání více záznamů v jednom volání — to je několikanásobně rychlejší než vkládání po jednom v cyklu. Pro hromadné operace použijte dávkové vkládání po 100–200 záznamech: to je optimální rovnováha mezi rychlostí provádění a spotřebou RAM na mobilních zařízeních s omezenými zdroji.
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();
Často kladené dotazy
sqflite vyžaduje ruční psaní SQL dotazů a mapování ResultSet na objekty. Floor generuje tento kód automaticky: popíšete Entity a DAO a typované metody vracejí hotové Dart objekty. Floor také kontroluje SQL dotazy při kompilaci prostřednictvím anotací.
Floor nemá vestavěné anotace pro vztahy (ForeignKey, @Relation) jako Room. Vztahy jsou implementovány prostřednictvím ručních SQL JOIN dotazů v @Query. Pro složitá relační schémata je lepší zvážit Drift s jeho vestavěnou podporou vztahů.
Floor umožňuje povolit callback callback při vytváření DatabaseBuilder — je mu předána instance sqflite.Database, na kterou lze připojit logger. Alternativně použijte floor_doctor pro vizualizaci schématu a dat v režimu vývoje.
Floor používá sqflite, který nefunguje v webovém prostředí. Pro web je potřeba samostatné sestavení s sqlite3 přes WASM. V aktuální verzi Floor oficiálně podporuje Android, iOS a macOS. Pro web použijte Drift s adaptérem sqlite3.
Floor nemá vestavěné cachování — každý dotaz se provádí k SQLite. Pro cachování opakujících se dotazů použijte vrstvu Repository s cache v paměti (například dart_cache). Floor pouze generuje kód pro práci s SQLite, aniž by přidával vrstvy nad ním.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také