Drift (Moor) — Vad är det, reaktiv ORM och arbete med databaser

Författare: IT Sectr Publicerad: 2026-03-13 Lästid: 9 min

Drift (tidigare Moor) — reaktiv ORM för Flutter och Dart, byggd ovanpå SQLite med eget DSL för frågor. Till skillnad från traditionella ORM kompilerar Drift Dart-frågor till SQL vid byggtid, vilket eliminerar runtime-fel. Enligt Drift Docs, 2024 genererar Drift upp till 40% mer kod än manuella SQL-frågor, men eliminerar helt manuell SQL-skrivning och ersätter den med typsäker Dart-syntax.

Huvudpunkter

  • Drift — reaktiv ORM med kompilering av frågor till SQL vid byggtid
  • DSL-frågor — fluent-gränssnitt i Dart utan att skriva SQL-strängar
  • Reaktivitet — Stream och auto-updating frågor för realtidsuppdateringar av UI
  • Plattformsoberoende — Android, iOS, Web, macOS, Linux, Windows
  • Migrationer — automatisk versionshantering och manuella migrationer via SQL

Vad är Drift?

Drift — ORM för Dart och Flutter, tidigare känt som Moor. Utvecklat av Simon Binder 2019 och har sedan dess gått igenom flera större versioner. Drift kompilerar Dart-frågor till SQL vid byggtid med hjälp av drift_dev och build_runner, vilket ger full typsäkerhet och eliminerar syntaxfel i SQL vid körning.

Till skillnad från Floor använder Drift sitt eget DSL (Domain-Specific Language) för att bygga frågor — utvecklaren skriver i Dart och generatorn översätter det till SQL. Detta låter IDE kontrollera syntax, autocompleta tabellfält och refaktorera datamodellen utan rädsla för att bryta frågan.

Enligt Drift (2024) används biblioteket i över 8000 Flutter-projekt. Det stöder alla populära plattformar: Android via sqflite, iOS via sqflite, web via sqlite3 WASM, desktop via sqlite3 native-drivrutin.

Namnbyteshistoria: Moor → Drift

Moor bytte namn till Drift i version 2.0 (2022). Anledningen var namnkollision med andra projekt och en önskan att distansera sig från gammal kod. API förblev kompatibelt: för migration räcker det att byta import från moor till drift och uppdatera beroenden.

Nyckelfunktioner

Drift erbjuder: inbyggda Stream-frågor med automatisk uppdatering vid dataändringar, stöd för transaktioner med återrullning, anpassade SQL-frågor via rawQuery, DAO-mönster för inkapsling av logik, plattformsoberoende migrationer och integration med Riverpod och BLoC via paketen drift_riverpod och drift_bloc.

Hur fungerar Drift?

Drift använder kodgenerering vid kompilering. Utvecklaren beskriver tabeller via @DataClass-annotationer eller Dart-klasser som utökar Table. Generatorn skapar hjälpklasser: Companion (för nullable fält vid insättning/uppdatering), DriftDatabase (startpunkt) och DAO-implementationer.

Drift utför inte SQLite-frågor direkt. Istället skriver utvecklaren i Dart: select(tasks).where(tasks.priority.greaterThan(3)).build(). Generatorn översätter detta till SQL, och vid körning skickar Drift helt enkelt den färdiga SQL-frågan till SQLite. Detta kombinerar bekvämligheten med Dart-syntax med prestandan från native SQL.

Frågarkitektur

Drift stöder två frågelägen: DSL (rekommenderat) och raw SQL. DSL-frågor är säkrare att skriva — kompilatorn kontrollerar fältnamn, typer och kompatibilitet. Raw SQL behövs för komplexa frågor som inte täcks av DSL: fönsterfunktioner, rekursiva CTE, specifika SQLite-tillägg.

Drift DSL vs SQL: jämförelse av tillvägagångssätt

Drift erbjuder två sätt att skriva frågor: Dart DSL (native) och raw SQL (för komplexa fall). DSL är att föredra för 90% av scenarierna: det är säkrare, mer läsbart och stöder refaktorering. Raw SQL används endast när DSL inte täcker den önskade konstruktionen.

AspektDrift DSLRaw SQL i Drift
TypsäkerhetFull (kompilering)Nej (runtime)
AutokompletteringJa (IDE)Endast i .sql-filer
RefaktoreringAutomatiskManuell sökning i strängar
Komplexa JOINStödsFull frihet
FönsterfunktionerBegränsatFullt stöd
ReaktivitetInbyggd (Stream)Via .watch()

När ska DSL användas

Drift DSL — det primära arbetssättet. Det täcker SELECT, INSERT, UPDATE, DELETE, WHERE, ORDER BY, LIMIT, JOIN och gruppering. För alla typiska CRUD-frågor, använd DSL: det är kortare, säkrare och uppdaterar automatiskt Stream vid ändringar.

När ska raw SQL användas

Raw SQL i Drift behövs för: anpassade SQLite-funktioner (FTS5, JSON1), komplexa underfrågor med EXISTS, INSERT OR REPLACE, massuppdateringar med CASE, samt för frågor där prestanda är kritisk och DSL inte genererar en optimal exekveringsplan. Raw SQL kan skrivas i .sql-filer med typstöd via drift_dev.

Kodexempel med Drift

Drift använder klasser som utökar Table eller @DataClass-annotationen. Nedan är ett komplett exempel på en Task-modell med frågor via DSL, raw SQL och reaktiv uppdatering. Efter att ha kört build_runner är alla genererade klasser redo.

Definition av tabell och databas

Klassen Tasks utökar Table och definierar kolumner. Varje kolumn är ett uttryck av typen Column<T>. Parametrar: withDefault() anger standardvärde, autoIncrement() — autoinkrement. Databasen är en abstrakt klass som utökar $DriftDatabase.

dart
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);
}

CRUD-operationer via DSL

Drift genererar metoderna into(tasks).insert(), select(tasks), update(tasks) och delete(tasks) för tabeller. Alla operationer returnerar Future — arbete med SQLite är asynkront. För att spåra ändringar, använd .watch() istället för .get().

dart
// Infoga
await into(tasks).insert(TasksCompanion.insert(
    title: Value('Köp matvaror'),
    priority: Value(3),
));

// Läs med filter
final highPriority = await (select(tasks)
    ..where((t) => t.priority.greaterThan(2))
    ..orderBy([(t) => OrderingTerm(expression: t.priority, mode: OrderingMode.desc)]))
    .get();

// Reaktiv observation
select(tasks).watch().listen((tasksList) {
    // tasksList — List, uppdateras vid varje tabelländring
    updateUi(tasksList);
});

Raw SQL med typat resultat

För komplexa frågor tillåter Drift att skriva raw SQL samtidigt som typningen bevaras. Metoden customSelect tar emot en frågesträng och returnerar ett typat resultat via kodgeneratorn. Detta tillvägagångssätt kombinerar SQL-flexibilitet med Drifts typsäkerhet.

dart
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")}');
}

Migrationer och versionshantering i Drift

Drift stöder både automatiska migrationer (för enkla ändringar) och manuella (för komplexa transformationer). Databasversionen ställs in i AppDatabases konstruktor. Vid versionsmissmatchning tillämpar Drift alla väntande migrationer i följd.

Automatiska migrationer

För att lägga till en kolumn med standardvärde kan Drift generera en migration automatiskt via MigrationStrategy. Om ändringen inte påverkar befintlig data (lägga till nullable-fält), kan beforeOpen användas med versionskontroll och exekvering av ALTER TABLE.

Manuella migrationer

För komplexa ändringar (ombenämning av tabell, sammanslagning av data, ändring av kolumntyp) kräver Drift manuell SQL-migration. Migrationerna anges via parametern migrations i databasklassen. Varje migration är ett objekt med from/to-nummer och SQL-frågor.

Drift och testning

Drift stöder körning i testläge via NativeDatabase.memory(). Databasen i minnet skapas från grunden före varje test och förstörs efteråt. För mockning, använd paketet mocktail med mocked QueryExecutor. Drift tillhandahåller också DatabaseTestHelper för integrationstester med kontroll av migrationer och frågor.

DAO-mönster i Drift

Drift stöder DAO (Data Access Object) via abstrakta klasser med @DriftAccessor-annotationen. DAO kapslar in frågor till en eller flera tabeller och kan testas oberoende av databasen. Till skillnad från direkta frågor via Database tillåter DAO återanvändning av frågelogik mellan olika delar av applikationen och förenklar enhetstestning.

dart
@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);
        }));
    }
}

Vanliga frågor

Vad skiljer Drift från Floor?

Drift använder eget DSL istället för SQL-strängar, vilket ger full typsäkerhet och autokomplettering i IDE. Floor använder SQL-strängar i @Query-annotationen. Drift stöder också fler plattformar (inklusive webben) och har inbyggd reaktivitet via Stream, medan Stream i Floor måste deklareras manuellt.

Stöder Drift migrationer utan dataförlust?

Ja, Drift stöder migrationer med databevarande. För att lägga till kolumner, använd addColumn i Migration. För komplexa transformationer (ombenämning, sammanslagning), skriv raw SQL inuti migrationen. Om ingen migration anges, återskapar Drift databasen med dataförlust vid schema-missmatchning.

Kan Drift användas utan build_runner?

Drift kräver kodgenerering via build_runner och drift_dev. Utan generering går det inte att skapa typade frågor. För små projekt stöder dock Drift sqlparser — manuell skrivning av SQL-filer med automatisk typning, men detta kräver fortfarande ett genereringssteg.

Hur integrerar man Drift med Riverpod?

För integration med Riverpod, använd paketet drift_riverpod. Det tillhandahåller providers för Database, DAO och Stream-frågor. Exempel: final tasksProvider = databaseProvider.select((db) => db.select(db.tasks).watch()) — UI byggs automatiskt om vid dataändringar.

Stöder Drift SQLite-kryptering?

Drift har ingen inbyggd kryptering, men stöder anslutning av anpassade sqlite3-bibliotek med SEE (SQLite Encryption Extension). För mobila plattformar, använd sqflite_sqlcipher som QueryExecutor — Drift fungerar med alla SQLite-implementationer via den abstrakta QueryExecutor.

Sammanfattning

  • Drift — reaktiv ORM för Flutter och Dart med kompilering av frågor till SQL vid byggtid
  • DSL-syntax — Dart-frågor med full typsäkerhet och autokomplettering i IDE
  • Reaktivitet — Stream och auto-updating frågor för automatiska UI-uppdateringar
  • Plattformsoberoende — Android, iOS, Web (WASM), macOS, Linux, Windows
  • Migrationer — automatiska för enkla ändringar och manuell SQL för komplexa
  • Ekosystem — integration med Riverpod (drift_riverpod) och BLoC (drift_bloc)
  • Rekommendation — välj Drift för projekt där reaktivitet, typsäkerhet och stöd för alla Flutter-plattformar är viktiga

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också