Drift (dating Moor) — reactive ORM para sa Flutter at Dart, na binuo sa ibabaw ng SQLite na may sariling DSL para sa mga query. Hindi tulad ng tradisyonal na ORM, ang Drift ay nagko-compile ng Dart queries sa SQL sa build time, na inaalis ang runtime errors. Ayon sa Drift Docs, 2024, ang Drift ay bumubuo ng hanggang 40% na mas maraming code kaysa sa manu-manong SQL queries, ngunit ganap nitong inaalis ang manu-manong pagsulat ng SQL, pinapalitan ito ng type-safe Dart syntax.
Mga Pangunahing Punto
Drift — ORM para sa Dart at Flutter, dating kilala bilang Moor. Binuo ni Simon Binder noong 2019 at dumaan sa ilang major versions mula noon. Nagko-compile ang Drift ng Dart queries sa SQL sa build time gamit ang drift_dev at build_runner, na nagbibigay ng kumpletong type-safety at inaalis ang syntax errors ng SQL sa runtime.
Hindi tulad ng Floor, gumagamit ang Drift ng sarili nitong DSL (Domain-Specific Language) para sa paggawa ng queries — ang developer ay nagsusulat sa Dart, at ang generator ay nagti-translate nito sa SQL. Ito ay nagpapahintulot sa IDE na suriin ang syntax, i-autocomplete ang mga table fields, at i-refactor ang data model nang walang takot na masira ang query.
Ayon sa Drift (2024), ang library ay ginagamit sa mahigit 8000 Flutter projects. Sinusuportahan nito ang lahat ng sikat na platform: Android sa pamamagitan ng sqflite, iOS sa pamamagitan ng sqflite, web sa pamamagitan ng sqlite3 WASM, desktop sa pamamagitan ng sqlite3 native driver.
Moor ay pinalitan ng pangalan sa Drift sa version 2.0 (2022). Dahilan — conflict ng pangalan sa iba pang proyekto at pagnanais na lumayo sa lumang code. Ang API ay nanatiling compatible: para sa migration, sapat na ang palitan ang import mula moor tungo sa drift at i-update ang dependencies.
Drift ay nagbibigay ng: built-in Stream queries na may awtomatikong pag-update kapag nagbago ang data, suporta sa transactions na may rollback, custom SQL queries sa pamamagitan ng rawQuery, DAO pattern para sa encapsulation ng logic, cross-platform migrations at integration sa Riverpod at BLoC sa pamamagitan ng drift_riverpod at drift_bloc packages.
Drift ay gumagamit ng code generation sa compile time. Inilalarawan ng developer ang mga table sa pamamagitan ng @DataClass annotations o Dart classes na nag-e-extend ng Table. Ang generator ay lumilikha ng mga helper classes: Companion (para sa nullable fields sa insert/update), DriftDatabase (entry point) at DAO implementations.
Ang SQLite queries ay hindi direktang ginagawa ng Drift. Sa halip, ang developer ay nagsusulat sa Dart: select(tasks).where(tasks.priority.greaterThan(3)).build(). Ang generator ay nagti-translate nito sa SQL, at sa runtime ang Drift ay nagpapadala lang ng handa nang SQL query sa SQLite. Ito ay pinagsasama ang convenience ng Dart syntax sa performance ng native SQL.
Drift ay sumusuporta sa dalawang mode ng query: DSL (inirerekomenda) at raw SQL. Ang DSL queries ay mas ligtas isulat — sinusuri ng compiler ang field names, types at compatibility. Ang Raw SQL ay kailangan para sa mga kumplikadong query na hindi sakop ng DSL: window functions, recursive CTE, specific SQLite extensions.
Drift ay nag-aalok ng dalawang paraan ng pagsulat ng query: Dart DSL (native) at raw SQL (para sa mga kumplikadong kaso). Ang DSL ay mas mainam para sa 90% ng mga scenario: ito ay mas ligtas, mas nababasa at sumusuporta sa refactoring. Ang Raw SQL ay ginagamit lang kapag hindi sakop ng DSL ang kinakailangang construction.
| Aspekto | Drift DSL | Raw SQL sa Drift |
|---|---|---|
| Type-safety | Kumpleto (compilation) | Wala (runtime) |
| Autocomplete | Oo (IDE) | Sa .sql files lang |
| Refactoring | Awtomatiko | Manual na paghahanap sa strings |
| Complex JOIN | Sinusuportahan | Ganap na kalayaan |
| Window functions | Limitado | Ganap na suporta |
| Reactivity | Built-in (Stream) | Sa pamamagitan ng .watch() |
Drift DSL — ang pangunahing paraan ng paggawa. Sinasaklaw nito ang SELECT, INSERT, UPDATE, DELETE, WHERE, ORDER BY, LIMIT, JOIN at grouping. Para sa lahat ng tipikal na CRUD queries, gamitin ang DSL: ito ay mas maikli, mas ligtas at awtomatikong nag-a-update ng Stream kapag may pagbabago.
Raw SQL sa Drift ay kailangan para sa: custom SQLite functions (FTS5, JSON1), complex subqueries na may EXISTS, INSERT OR REPLACE, mass UPDATE na may CASE, at para sa mga query kung saan kritikal ang performance at hindi nagge-generate ang DSL ng optimal execution plan. Ang Raw SQL ay maaaring isulat sa .sql files na may typing support sa pamamagitan ng drift_dev.
Drift ay gumagamit ng mga class na nag-e-extend ng Table, o @DataClass annotation. Nasa ibaba ang kumpletong halimbawa ng Task model na may queries sa pamamagitan ng DSL, raw SQL at reactive update. Pagkatapos ng build_runner, lahat ng generated classes ay handa na.
Ang klase na Tasks ay nag-e-extend ng Table at nagde-deklara ng mga column. Ang bawat column ay expression ng uri Column<T>. Parameters: withDefault() ay nagtatakda ng default value, autoIncrement() — auto-increment. Ang database ay abstract class na nag-e-extend ng $DriftDatabase.
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);
}
Ang Drift ay bumubuo ng mga method na into(tasks).insert(), select(tasks), update(tasks) at delete(tasks) para sa mga table. Lahat ng operations ay nagre-return ng Future — ang paggawa sa SQLite ay asynchronous. Para sa pagsubaybay ng mga pagbabago, gamitin ang .watch() sa halip na .get().
// Insert
await into(tasks).insert(TasksCompanion.insert(
title: Value('Buy groceries'),
priority: Value(3),
));
// Read with filter
final highPriority = await (select(tasks)
..where((t) => t.priority.greaterThan(2))
..orderBy([(t) => OrderingTerm(expression: t.priority, mode: OrderingMode.desc)]))
.get();
// Reactive observation
select(tasks).watch().listen((tasksList) {
// tasksList — List, ina-update sa bawat pagbabago ng table
updateUi(tasksList);
});
Para sa mga kumplikadong query, pinapayagan ng Drift ang pagsulat ng raw SQL habang pinapanatili ang typing. Ang method na customSelect ay tumatanggap ng query string at nagre-return ng typed na resulta sa pamamagitan ng code generator. Ang ganitong approach ay pinagsasama ang flexibility ng SQL sa type safety ng 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 ay sumusuporta sa parehong awtomatikong migrations (para sa simpleng pagbabago) at manual (para sa kumplikadong transformations). Ang version ng database ay itinakda sa constructor ng AppDatabase. Kapag hindi tugma ang version, ina-apply ng Drift ang lahat ng hindi pa naa-apply na migrations nang sunod-sunod.
Para sa pagdagdag ng column na may default value, ang Drift ay maaaring bumuo ng migration awtomatiko sa pamamagitan ng MigrationStrategy. Kung ang pagbabago ay hindi makakaapekto sa existing data (pagdagdag ng nullable field), maaaring gamitin ang beforeOpen na may version check at pag-execute ng ALTER TABLE.
Para sa kumplikadong pagbabago (pagpapalit ng pangalan ng table, pagsasama ng data, pagbabago ng column type), ang Drift ay nangangailangan ng manual SQL migration. Ang migrations ay itinakda sa pamamagitan ng parameter na migrations sa database class. Ang bawat migration ay object na may from/to numbers at SQL queries.
Drift ay sumusuporta sa pagtakbo sa test mode sa pamamagitan ng NativeDatabase.memory(). Ang in-memory database ay nilikha mula sa simula bago ang bawat test at sinisira pagkatapos. Para sa mocking, gamitin ang mocktail package na may mocked QueryExecutor. Nagbibigay din ang Drift ng DatabaseTestHelper para sa integration tests na may checking ng migrations at queries.
Drift ay sumusuporta sa DAO (Data Access Object) sa pamamagitan ng abstract classes na may @DriftAccessor annotation. Ang DAO ay nag-e-encapsulate ng queries sa isa o maraming table at maaaring i-test nang hiwalay sa database. Hindi tulad ng direktang queries sa pamamagitan ng Database, pinapayagan ng DAO ang pag-reuse ng query logic sa iba't ibang bahagi ng application at pinapasimple ang unit testing.
@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);
}));
}
}
Mga Madalas Itanong
Drift ay gumagamit ng sarili nitong DSL sa halip na SQL strings, na nagbibigay ng kumpletong type-safety at autocomplete sa IDE. Ang Floor ay gumagamit ng SQL strings sa @Query annotation. Sinusuportahan din ng Drift ang mas maraming platform (kabilang ang web) at may built-in reactivity sa pamamagitan ng Stream, samantalang sa Floor ang Stream ay kailangang ideklara nang manu-mano.
Oo, Drift ay sumusuporta sa migrations na may pangangalaga ng data. Para sa pagdagdag ng columns, gamitin ang addColumn sa Migration. Para sa kumplikadong transformations (pagpapalit ng pangalan, pagsasama), sumulat ng raw SQL sa loob ng migration. Kung walang migration na tinukoy, ang Drift ay muling lilikha ng database na may pagkawala ng data kapag hindi tugma ang schema.
Drift ay nangangailangan ng code generation sa pamamagitan ng build_runner at drift_dev. Kung walang generation, hindi posible na lumikha ng typed queries. Gayunpaman, para sa maliliit na proyekto, sinusuportahan ng Drift ang sqlparser — manual na pagsulat ng SQL files na may awtomatikong typing, ngunit ito ay nangangailangan pa rin ng generation stage.
Para sa integration sa Riverpod, gamitin ang drift_riverpod package. Nagbibigay ito ng mga provider para sa Database, DAO at Stream queries. Halimbawa: final tasksProvider = databaseProvider.select((db) => db.select(db.tasks).watch()) — awtomatikong nagre-rebuild ang UI kapag nagbago ang data.
Drift ay walang built-in encryption, ngunit sinusuportahan nito ang pag-connect ng custom sqlite3 libraries na may SEE (SQLite Encryption Extension). Para sa mobile platforms, gamitin ang sqflite_sqlcipher bilang QueryExecutor — gumagana ang Drift sa kahit anong SQLite implementation sa pamamagitan ng abstract QueryExecutor.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din