Floor — ORM (Object-Relational Mapping) pour Flutter, fournissant une couche typée au-dessus de SQLite. Contrairement aux requêtes SQLite brutes, Floor génère des classes DAO à partir de modèles Dart annotés. Selon Pub.dev, 2024, Floor est utilisé dans plus de 3500 projets Flutter et fait partie des trois solutions ORM les plus populaires pour le stockage local de données avec drift et hive.
Points Clés
Floor est une bibliothèque ORM pour Flutter et Dart construite sur SQLite. Elle utilise des annotations pour décrire les entités (Entity), les Data Access Objects (DAO) et la base de données (Database). La génération de code est effectuée via build_runner et floor_generator — le compilateur crée des implémentations DAO et une classe de gestion de base de données. Contrairement à sqflite brut, Floor élimine complètement le besoin de conversion manuelle de ResultSet en objets Dart en mappant automatiquement les colonnes vers les champs Entity par réflexion de types.
Floor suit le patron Repository + DAO familier aux développeurs Android avec Room. Chaque table est représentée par une classe Dart avec l’annotation @Entity, les requêtes SQL sont regroupées dans des interfaces avec l’annotation @dao, et la base de données est assemblée dans une classe abstraite avec @Database. Cette approche sépare strictement le modèle de données de la logique de requêtes.
Selon Flutter Pulse (2023), Floor est choisi dans 28 % des projets Flutter nécessitant une base de données locale. Les principales raisons de ce choix sont la familiarité avec SQL (pas besoin d’apprendre un nouveau langage de requêtes) et la vérification des requêtes à la compilation. De plus, Floor génère un code lisible facile à déboguer par rapport aux ORM plus abstraits avec DSL personnalisés, ce qui abaisse la barrière à l’entrée pour les nouveaux développeurs dans l’équipe.
Floor se compose de trois couches : Entity (modèle de table), DAO (interface de requêtes) et Database (point d’entrée). Le générateur crée des implémentations _$_Entity pour le mappage de champs et _$_Dao pour l’exécution SQL. Lorsque Entity ou DAO change, il suffit de redémarrer build_runner — le code se met à jour automatiquement. Pour la migration entre versions de schéma, Floor utilise des numéros de version séquentiels, garantissant l’intégrité des données lors de la mise à jour de l’application sur les appareils des utilisateurs.
Floor utilise SQLite via le paquet sqflite pour les builds natifs et sqlite3 pour le bureau et le web. Au démarrage de l’application, Floor crée ou ouvre le fichier SQLite, applique les migrations et prépare les méthodes DAO pour exécuter les requêtes. Toutes les opérations sont effectuées de manière asynchrone via Future et Stream.
La génération de code dans Floor fonctionne comme suit : l’analyseur lit les annotations des fichiers source, crée un AST (Arbre Syntaxique Abstrait) des modèles et requêtes, puis génère des fichiers Dart avec le préfixe _$. Le code généré inclut les mappeurs ResultSet → Entity et vice versa.
Floor travaille avec SQLite dans un seul isolate. Toutes les requêtes sont exécutées de manière asynchrone, mais les écritures concurrentes sont verrouillées au niveau SQLite. Pour les transactions, l’annotation @transaction est utilisée, garantissant l’atomicité d’un groupe de requêtes et le rollback en cas d’erreur.
Floor et Drift sont tous deux des ORM sur SQLite, mais ils diffèrent par leur philosophie. Floor est plus proche de Room d’Android, Drift est plus réactif avec une API Stream intégrée et une compilation de requêtes via des fichiers SQL. Le choix entre eux dépend de l’expérience de l’équipe et de la réactivité requise.
| Caractéristique | Floor | Drift |
|---|---|---|
| Type de requêtes | Chaînes SQL dans @Query | Méthodes Dart + fichiers sql |
| Génération de code | floor_generator (build_runner) | drift_dev (build_runner) |
| Réactivité | Stream depuis DAO | API Stream intégrée + mise à jour automatique |
| Complexité | Faible (SQL familier) | Moyenne (DSL propre) |
| Migrations | Scripts SQL manuels | Automatiques + manuelles |
| Compatibilité | Android, iOS, macOS | Android, iOS, Web, macOS, Linux |
Floor est le choix pour les équipes déjà familières avec SQL et Android Room. Si les développeurs ont l’habitude d’écrire des requêtes SQL manuellement et souhaitent une enveloppe minimale sur SQLite — Floor offre le typage sans apprendre un nouveau DSL. Il est également plus facile à déboguer car le code généré est lisible et prévisible.
Drift offre une réactivité plus puissante et supporte davantage de plateformes. Si l’application utilise activement Stream pour mettre à jour l’UI, nécessite des requêtes complexes avec JOIN et sous-requêtes ou cible le web — Drift est préférable. Cependant, sa barrière à l’entrée est plus élevée en raison de la nécessité d’apprendre son propre DSL.
Floor est construit autour d’annotations. Vous trouverez ci-dessous un exemple complet d’Entity, DAO et Database pour une application de liste de tâches. Après avoir exécuté build_runner, les classes générées sont prêtes à l’emploi.
La classe TaskEntity avec l’annotation @Entity est mappée sur la table task. Un champ avec @primaryKey devient la clé primaire. L’interface TaskDao contient des méthodes pour les opérations sur la table — chaque méthode est annotée avec @Query, @Insert, @Update ou @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);
}
Une classe abstraite avec l’annotation @Database lie Entity et DAO. La méthode databaseBuilder crée une instance de base de données. Après avoir appelé build, la base de données est prête : Floor ouvre le fichier SQLite, applique les migrations et retourne le DAO pour le travail.
@Database(version: 1, entities: [TaskEntity])
abstract class AppDatabase extends FloorDatabase {
TaskDao get taskDao;
}
// Utilisation
final database = await $FloorAppDatabase.databaseBuilder('app.db').build();
final taskDao = database.taskDao;
final tasks = await taskDao.getAllTasks();
Floor prend en charge le retour de Stream depuis les méthodes DAO. Lors de toute modification dans la table, le Stream émet une nouvelle liste. Cela s’intègre avec StreamBuilder dans Flutter — l’UI se met automatiquement à jour lors de l’ajout, la modification ou la suppression d’enregistrements.
@Query('SELECT * FROM TaskEntity ORDER BY priority DESC')
Stream<List<TaskEntity>> watchAllTasks();
// Dans le widget 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 prend en charge le versionnage de base de données via le paramètre version dans l’annotation @Database. Lorsque Entity change (ajout ou suppression de champs), vous devez incrémenter la version et ajouter une migration. Une migration est une fonction Dart qui reçoit une transaction et exécute des requêtes SQL ALTER TABLE.
Supposons qu’à la version 2 nous ayons ajouté un champ dueDate à TaskEntity. La migration est effectuée via une requête SQL ALTER TABLE. Si une migration n’est pas spécifiée, Floor appelle MigrationStrategy où vous pouvez définir un fallback (par exemple, recréer la table avec perte de données).
Floor ne fournit pas de framework mock intégré, mais la base de données peut facilement être remplacée dans les tests. Créez un inMemoryDatabaseBuilder — il crée une base de données SQLite en mémoire identique en schéma à la production. Après chaque test, nettoyez les données via deleteDatabase pour isoler les scénarios de test.
Floor prend en charge les transactions via l’annotation @transaction sur les méthodes DAO. À l’intérieur d’une transaction, plusieurs requêtes sont exécutées séquentiellement avec garantie de rollback en cas d’erreur. L’insertion par lots via @Insert avec un paramètre 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();
Foire Aux Questions
sqflite nécessite l’écriture manuelle de requêtes SQL et le mappage de ResultSet vers des objets. Floor génère ce code automatiquement : vous décrivez Entity et DAO, et les méthodes typées retournent des objets Dart prêts à l’emploi. Floor vérifie également les requêtes SQL à la compilation via les annotations.
Floor ne dispose pas d’annotations intégrées pour les relations (ForeignKey, @Relation) comme Room. Les relations sont implémentées via des requêtes SQL JOIN manuelles dans @Query. Pour les schémas relationnels complexes, envisagez Drift avec sa prise en charge intégrée des relations.
Floor permet d’activer un callback lors de la création de DatabaseBuilder — il reçoit une instance de sqflite.Database sur laquelle vous pouvez attacher un logger. Alternativement, utilisez floor_doctor pour visualiser le schéma et les données en mode dev.
Floor utilise sqflite, qui ne fonctionne pas dans un environnement web. Pour le web, un build séparé avec sqlite3 via WASM est nécessaire. Dans la version actuelle, Floor supporte officiellement Android, iOS et macOS. Pour le web, utilisez Drift avec l’adaptateur sqlite3.
Floor n’a pas de cache intégré — chaque requête est exécutée sur SQLite. Pour mettre en cache les requêtes répétées, utilisez une couche Repository avec cache en mémoire (par exemple, dart_cache). Floor génère uniquement le code pour travailler avec SQLite sans ajouter de surcharge.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi