Floor — ORM (Object-Relational Mapping) für Flutter, das eine typisierte Schicht über SQLite bereitstellt. Im Gegensatz zu rohen SQLite-Abfragen generiert Floor DAO-Klassen aus annotierten Dart-Modellen. Laut Pub.dev, 2024 wird Floor in über 3500 Flutter-Projekten verwendet und gehört neben drift und hive zu den drei beliebtesten ORM-Lösungen für die lokale Datenspeicherung.
Wichtige Punkte
Floor ist eine ORM-Bibliothek für Flutter und Dart, die auf SQLite aufbaut. Sie verwendet Annotationen, um Entitäten (Entity), Data Access Objects (DAO) und die Datenbank (Database) zu beschreiben. Die Codegenerierung erfolgt über build_runner und floor_generator — der Compiler erstellt DAO-Implementierungen und eine verwaltende Datenbankklasse. Im Gegensatz zu rohem sqflite eliminiert Floor vollständig die Notwendigkeit einer manuellen Konvertierung von ResultSet in Dart-Objekte, indem es Spalten durch Typreflexion automatisch auf Entity-Felder abbildet.
Floor folgt dem Repository + DAO-Muster, das Android-Entwicklern von Room bekannt ist. Jede Tabelle wird durch eine Dart-Klasse mit der @Entity-Annotation repräsentiert, SQL-Abfragen werden in Schnittstellen mit der @dao-Annotation gruppiert und die Datenbank wird in einer abstrakten Klasse mit @Database zusammengestellt. Dieser Ansatz trennt das Datenmodell strikt von der Abfragelogik.
Laut Flutter Pulse (2023) wird Floor in 28% der Flutter-Projekte gewählt, die eine lokale Datenbank benötigen. Die Hauptgründe für die Wahl sind Vertrautheit mit SQL (keine Notwendigkeit, eine neue Abfragesprache zu lernen) und die Compile-Zeit-Überprüfung von Abfragen. Darüber hinaus generiert Floor lesbaren Code, der im Vergleich zu abstrakteren ORMs mit benutzerdefinierten DSLs einfach zu debuggen ist, was die Einstiegshürde für neue Entwickler im Team senkt.
Floor besteht aus drei Schichten: Entity (Tabellenmodell), DAO (Abfrageschnittstelle) und Database (Einstiegspunkt). Der Generator erstellt _$_Entity-Implementierungen für die Feldzuordnung und _$_Dao zur Ausführung von SQL. Wenn sich Entity oder DAO ändern, reicht ein Neustart von build_runner — der Code wird automatisch aktualisiert. Für die Migration zwischen Schema-Versionen verwendet Floor sequenzielle Versionsnummern, was die Datenintegrität bei der Aktualisierung der App auf Benutzergeräten gewährleistet.
Floor verwendet SQLite über das sqflite-Paket für Plattform-Builds und sqlite3 für Desktop und Web. Wenn die App startet, erstellt oder öffnet Floor die SQLite-Datei, wendet Migrationen an und bereitet DAO-Methoden zur Ausführung von Abfragen vor. Alle Operationen werden asynchron über Future und Stream ausgeführt.
Die Codegenerierung in Floor funktioniert wie folgt: Der Parser liest Annotationen aus den Quelldateien, erstellt einen AST (Abstract Syntax Tree) von Modellen und Abfragen und generiert dann Dart-Dateien mit dem Präfix _$. Der generierte Code enthält ResultSet-ÄäEntity-Mapper und umgekehrt.
Floor arbeitet mit SQLite in einem einzigen Isolat. Alle Abfragen werden asynchron ausgeführt, aber gleichzeitige Schreibvorgänge werden auf SQLite-Ebene gesperrt. Für Transaktionen wird die @transaction-Annotation verwendet, die die Atomizität einer Gruppe von Abfragen und den Rollback bei Fehlern garantiert.
Sowohl Floor als auch Drift sind ORMs über SQLite, unterscheiden sich jedoch in der Philosophie. Floor ist näher an Room von Android, Drift ist reaktiver mit integrierter Stream-API und Abfragekompilierung über SQL-Dateien. Die Wahl zwischen ihnen hängt von der Teamerfahrung und der erforderlichen Reaktivität ab.
| Eigenschaft | Floor | Drift |
|---|---|---|
| Abfragetyp | SQL-Strings in @Query | Dart-Methoden + SQL-Dateien |
| Codegenerierung | floor_generator (build_runner) | drift_dev (build_runner) |
| Reaktivität | Stream vom DAO | Integrierte Stream-API + Auto-Update |
| Komplexität | Niedrig (vertrautes SQL) | Mittel (eigenes DSL) |
| Migrationen | Manuelle SQL-Skripte | Automatisch + manuell |
| Kompatibilität | Android, iOS, macOS | Android, iOS, Web, macOS, Linux |
Floor ist die Wahl für Teams, die bereits mit SQL und Android Room vertraut sind. Wenn Entwickler es gewohnt sind, SQL-Abfragen manuell zu schreiben und einen minimalen Wrapper über SQLite wünschen — bietet Floor Typisierung ohne Erlernen eines neuen DSL. Es ist auch einfacher zu debuggen, da der generierte Code lesbar und vorhersagbar ist.
Drift bietet leistungsstärkere Reaktivität und unterstützt mehr Plattformen. Wenn die App aktiv Stream für UI-Updates nutzt, komplexe Abfragen mit JOIN und Unterabfragen erfordert oder auf Web abzielt — ist Drift vorzuziehen. Allerdings ist die Einstiegshürde aufgrund der Notwendigkeit, das eigene DSL zu erlernen, höher.
Floor ist um Annotationen herum aufgebaut. Nachfolgend finden Sie ein vollständiges Beispiel für Entity, DAO und Database für eine Aufgabenlisten-App. Nach dem Ausführen von build_runner sind die generierten Klassen einsatzbereit.
Die TaskEntity-Klasse mit @Entity-Annotation wird auf die Tabelle task abgebildet. Ein Feld mit @primaryKey wird zum Primärschlüssel. Das TaskDao-Interface enthält Methoden für Tabellenoperationen — jede Methode ist mit @Query, @Insert, @Update oder @Delete annotiert.
@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);
}
Eine abstrakte Klasse mit der @Database-Annotation verbindet Entity und DAO. Die databaseBuilder-Methode erstellt eine Datenbankinstanz. Nach dem Aufruf von build ist die Datenbank bereit: Floor öffnet die SQLite-Datei, wendet Migrationen an und gibt das DAO zur Arbeit zurück.
@Database(version: 1, entities: [TaskEntity])
abstract class AppDatabase extends FloorDatabase {
TaskDao get taskDao;
}
// Verwendung
final database = await $FloorAppDatabase.databaseBuilder('app.db').build();
final taskDao = database.taskDao;
final tasks = await taskDao.getAllTasks();
Floor unterstützt die Rückgabe von Stream aus DAO-Methoden. Bei Änderungen in der Tabelle sendet der Stream eine neue Liste. Dies integriert sich mit StreamBuilder in Flutter — die UI wird automatisch aktualisiert, wenn Datensätze hinzugefügt, geändert oder gelöscht werden.
@Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
Stream<List<TaskEntity>> watchAllTasks();
// Im 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 unterstützt die Datenbankversionierung über den Parameter version in der @Database-Annotation. Wenn sich Entity ändert (Hinzufügen oder Entfernen von Feldern), müssen Sie die Version erhöhen und eine Migration hinzufügen. Eine Migration ist eine Dart-Funktion, die eine Transaktion erhält und ALTER TABLE SQL-Abfragen ausführt.
Angenommen, in Version 2 haben wir ein dueDate-Feld zu TaskEntity hinzugefügt. Die Migration wird über eine ALTER TABLE SQL-Abfrage durchgeführt. Wenn keine Migration angegeben ist, ruft Floor MigrationStrategy auf, wo Sie einen Fallback festlegen können (z. B. Neuerstellung der Tabelle mit Datenverlust).
Floor bietet kein integriertes Mock-Framework, aber die Datenbank kann in Tests einfach ersetzt werden. Erstellen Sie einen inMemoryDatabaseBuilder — er erstellt eine SQLite-Datenbank im Speicher, die im Schema identisch zur Produktion ist. Nach jedem Test löschen Sie die Daten über deleteDatabase, um Testszenarien zu isolieren.
Floor unterstützt Transaktionen über die @transaction-Annotation auf DAO-Methoden. Innerhalb einer Transaktion werden mehrere Abfragen sequenziell mit Rollback-Garantie bei Fehlern ausgeführt. Die Batch-Einfügung über @Insert mit einem 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();
Häufig gestellte Fragen
sqflite erfordert manuelles Schreiben von SQL-Abfragen und Mapping von ResultSet auf Objekte. Floor generiert diesen Code automatisch: Sie beschreiben Entity und DAO, und typisierte Methoden geben gebrauchsfertige Dart-Objekte zurück. Floor überprüft SQL-Abfragen auch zur Compile-Zeit über Annotationen.
Floor verfügt nicht über integrierte Annotationen für Beziehungen (ForeignKey, @Relation) wie Room. Beziehungen werden über manuelle SQL JOIN-Abfragen in @Query implementiert. Für komplexe relationale Schemata sollten Sie Drift mit seiner integrierten Beziehungsunterstützung in Betracht ziehen.
Floor ermöglicht die Aktivierung eines callback beim Erstellen von DatabaseBuilder — ihm wird eine Instanz von sqflite.Database übergeben, an die Sie einen Logger anhängen können. Alternativ verwenden Sie floor_doctor zur Visualisierung von Schema und Daten im Dev-Modus.
Floor verwendet sqflite, das in einer Web-Umgebung nicht funktioniert. Für das Web ist ein separater Build mit sqlite3 über WASM erforderlich. In der aktuellen Version unterstützt Floor offiziell Android, iOS und macOS. Für das Web verwenden Sie Drift mit dem sqlite3-Adapter.
Floor verfügt über kein integriertes Caching — jede Abfrage wird gegen SQLite ausgeführt. Zum Cachen wiederholter Abfragen verwenden Sie eine Repository-Schicht mit In-Memory-Cache (z. B. dart_cache). Floor generiert nur Code für die Arbeit mit SQLite, ohne zusätzlichen Overhead hinzuzufügen.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch