Drift (voorheen Moor) — reactieve ORM voor Flutter en Dart, gebouwd op SQLite met eigen DSL voor query’s. In tegenstelling tot traditionele ORM’s, compileert Drift Dart-query’s naar SQL tijdens de build-fase, waardoor runtime-fouten worden geëlimineerd. Volgens Drift Docs, 2024, genereert Drift tot 40% meer code dan handmatige SQL-query’s, maar elimineert het handmatig schrijven van SQL volledig door het te vervangen door type-safe Dart-syntax.
Belangrijkste
Drift — ORM voor Dart en Flutter, voorheen bekend als Moor. Ontwikkeld door Simon Binder in 2019 en heeft sindsdien meerdere grote versies doorgemaakt. Drift compileert Dart-query’s naar SQL tijdens de build-fase met behulp van drift_dev en build_runner, wat volledige typeveiligheid biedt en syntaxisfouten in SQL tijdens runtime elimineert.
In tegenstelling tot Floor gebruikt Drift zijn eigen DSL (Domain-Specific Language) voor het bouwen van query’s — de ontwikkelaar schrijft in Dart en de generator vertaalt dit naar SQL. Hierdoor kan de IDE de syntax controleren, tabelvelden automatisch aanvullen en het datamodel herstructureren zonder angst om de query te breken.
Volgens Drift (2024) wordt de bibliotheek gebruikt in meer dan 8000 Flutter-projecten. Het ondersteunt alle populaire platforms: Android via sqflite, iOS via sqflite, web via sqlite3 WASM, desktop via native sqlite3 driver.
Moor werd hernoemd naar Drift in versie 2.0 (2022). Reden — naamconflict met andere projecten en de wens om afstand te nemen van oude code. De API bleef compatibel: voor migratie volstaat het om de import van moor naar drift te wijzigen en de afhankelijkheden bij te werken.
Drift biedt: ingebouwde Stream-query’s met automatische updates bij gegevenswijzigingen, ondersteuning voor transacties met terugdraaien, aangepaste SQL-query’s via rawQuery, DAO-patroon voor inkapseling van logica, cross-platform migraties en integratie met Riverpod en BLoC via de pakketten drift_riverpod en drift_bloc.
Drift gebruikt codegeneratie tijdens de compilatiefase. De ontwikkelaar beschrijft tabellen via @DataClass-annotaties of Dart-klassen die Table uitbreiden. De generator maakt hulpklassen: Companion (voor nullable velden bij invoegen/bijwerken), DriftDatabase (ingangspunt) en DAO-implementaties.
SQLite-query’s worden niet direct door Drift uitgevoerd. In plaats daarvan schrijft de ontwikkelaar in Dart: select(tasks).where(tasks.priority.greaterThan(3)).build(). De generator vertaalt dit naar SQL en tijdens uitvoering stuurt Drift eenvoudigweg de kant-en-klare SQL-query naar SQLite. Dit combineert het gemak van Dart-syntax met de prestaties van native SQL.
Drift ondersteunt twee query-modi: DSL (aanbevolen) en raw SQL. DSL-query’s zijn veiliger — de compiler controleert veldnamen, typen en compatibiliteit. Raw SQL is nodig voor complexe query’s die niet door DSL worden gedekt: vensterfuncties, recursieve CTE’s, specifieke SQLite-extensies.
Drift biedt twee manieren om query’s te schrijven: Dart DSL (native) en raw SQL (voor complexe gevallen). DSL heeft de voorkeur in 90% van de scenario’s: het is veiliger, leesbaarder en ondersteunt refactoring. Raw SQL wordt alleen gebruikt wanneer DSL de vereiste constructie niet dekt.
| Aspect | Drift DSL | Raw SQL in Drift |
|---|---|---|
| Typeveiligheid | Volledig (compilatie) | Nee (runtime) |
| Automatisch aanvullen | Ja (IDE) | Alleen in sql-bestanden |
| Refactoring | Automatisch | Handmatig zoeken in strings |
| Complexe JOIN | Ondersteund | Volledige vrijheid |
| Vensterfuncties | Beperkt | Volledige ondersteuning |
| Reactiviteit | Ingebouwd (Stream) | Via .watch() |
Drift DSL — de belangrijkste werkwijze. Het dekt SELECT, INSERT, UPDATE, DELETE, WHERE, ORDER BY, LIMIT, JOIN en groepering. Gebruik DSL voor alle typische CRUD-query’s: het is korter, veiliger en werkt Stream automatisch bij bij wijzigingen.
Raw SQL is nodig in Drift voor: aangepaste SQLite-functies (FTS5, JSON1), complexe subquery’s met EXISTS, INSERT OR REPLACE, massale UPDATE met CASE, en query’s waar prestaties kritisch zijn en DSL geen optimaal uitvoeringsplan genereert. Raw SQL kan worden geschreven in .sql-bestanden met typeringsondersteuning via drift_dev.
Drift gebruikt klassen die Table uitbreiden of de @DataClass-annotatie. Hieronder — een compleet voorbeeld van het Task-model met query’s via DSL, raw SQL en reactieve updates. Na het uitvoeren van build_runner zijn alle gegenereerde klassen klaar.
De klasse Tasks breidt Table uit en definieert kolommen. Elke kolom is een expressie van het type Column
class Tasks extends Table {
IntColumn get id => integer().autoIncrement();
TextColumn get title => text().withDefault(const Constant(''))();
BoolColumn get isCompleted => boolean().withDefault(const Constant(false))();
IntColumn get priority => integer().withDefault(const Constant(0))();
}
@DriftDatabase(tables: [Tasks])
class AppDatabase extends $AppDatabase {
AppDatabase(QueryExecutor e) : super(e);
}
Drift genereert de methoden into(tasks).insert(), select(tasks), update(tasks) en delete(tasks) voor tabellen. Alle bewerkingen retourneren Future — werken met SQLite is asynchroon. Gebruik .watch() in plaats van .get() om wijzigingen te volgen.
// Schrijven
await into(tasks).insert(TasksCompanion.insert(
title: Value('Producten kopen'),
priority: Value(3),
));
// Lezen met filter
final highPriority = await (select(tasks)
..where((t) => t.priority.greaterThan(2))
..orderBy([(t) => OrderingTerm(expression: t.priority, mode: OrderingMode.desc)]))
.get();
// Reactief observeren
select(tasks).watch().listen((tasksList) {
// tasksList — List, wordt bij elke tabelwijziging bijgewerkt
updateUi(tasksList);
});
Voor complexe query’s staat Drift het schrijven van raw SQL toe, terwijl het typering behoudt. De methode customSelect accepteert een query-string en retourneert een getypeerd resultaat via de codegenerator. Deze aanpak combineert de flexibiliteit van SQL met de typeveiligheid van Drift.
final result = await customSelect(
'SELECT title, COUNT(*) as cnt FROM tasks GROUP BY title',
readsFrom: { tasks },
).get();
for (final row in result) {
print('${row.readString("title")}: ${row.readInt("cnt")}');
}
Drift ondersteunt zowel automatische migraties (voor eenvoudige wijzigingen) als handmatige (voor complexe transformaties). De databaseversie wordt ingesteld in de constructor van AppDatabase. Bij versieverschil past Drift alle openstaande migraties sequentieel toe.
Voor het toevoegen van een kolom met een standaardwaarde kan Drift automatisch een migratie genereren via MigrationStrategy. Als de wijziging bestaande gegevens niet schendt (toevoegen van een nullable veld), kan beforeOpen worden gebruikt met versiecontrole en uitvoering van ALTER TABLE.
Voor complexe wijzigingen (hernoemen van tabel, samenvoegen van gegevens, wijzigen van kolomtype) vereist Drift handmatige SQL-migratie. Migraties worden gespecificeerd via de parameter migrations in de databaseklasse. Elke migratie is een object met from/to-nummers en SQL-query’s.
Drift ondersteunt uitvoering in testmodus via NativeDatabase.memory(). De in-memory database wordt voor elke test vanaf nul aangemaakt en na de test vernietigd. Voor mocken gebruikt u het pakket mocktail met een gemockte QueryExecutor. Drift biedt ook DatabaseTestHelper voor integratietests met verificatie van migraties en query’s.
Drift ondersteunt DAO (Data Access Object) via abstracte klassen met de @DriftAccessor-annotatie. DAO kapselt query’s naar een of meerdere tabellen in en kan afzonderlijk van de database worden getest. In tegenstelling tot directe query’s via Database, maakt DAO hergebruik van querylogica tussen verschillende delen van de applicatie mogelijk en vereenvoudigt het modulair testen.
@DriftDatabase(tables: [Tasks])
class AppDatabase extends $AppDatabase {
AppDatabase(QueryExecutor e) : super(e) {
migrations.add(Migration(1, 2, (m) async {
await m.addColumn(tasks, tasks.dueDate);
await m.createIndex(tasks.idxPriority);
}));
}
}
Veelgestelde vragen
Drift gebruikt zijn eigen DSL in plaats van SQL-strings, wat volledige typeveiligheid en automatisch aanvullen in de IDE biedt. Floor gebruikt SQL-strings in de @Query-annotatie. Drift ondersteunt ook meer platforms (inclusief web) en heeft ingebouwde reactiviteit via Stream, terwijl in Floor Stream handmatig moet worden gedeclareerd.
Ja, Drift ondersteunt migraties met behoud van gegevens. Gebruik addColumn in Migration voor het toevoegen van kolommen. Voor complexe transformaties (hernoemen, samenvoegen) schrijft u raw SQL binnen de migratie. Als er geen migratie is opgegeven, herbouwt Drift de database met gegevensverlies bij schema-mismatch.
Drift vereist codegeneratie via build_runner en drift_dev. Zonder generatie kunnen er geen getypeerde query’s worden gemaakt. Voor kleine projecten ondersteunt Drift echter sqlparser — handmatig schrijven van SQL-bestanden met automatische typering, maar dit vereist nog steeds een generatiefase.
Gebruik voor integratie met Riverpod het pakket drift_riverpod. Het biedt providers voor Database, DAO en Stream-query’s. Voorbeeld: final tasksProvider = databaseProvider.select((db) => db.select(db.tasks).watch()) — de UI wordt automatisch herbouwd bij gegevenswijzigingen.
Drift heeft geen ingebouwde codering, maar ondersteunt het aansluiten van aangepaste sqlite3-bibliotheken met SEE (SQLite Encryption Extension). Gebruik voor mobiele platforms sqflite_sqlcipher als QueryExecutor — Drift werkt met elke SQLite-implementatie via de abstracte QueryExecutor.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook