agentleFS
ariaraminariaramin/flutter-skills/skills/flutter-drift-persistence-engineering/SKILL.md

flutter-drift-persistence-engineering

Skill8 starsChanged 48 days agoMITView on GitHub

Design, implement, review, debug, test, migrate, and optimize production Flutter persistence built with Drift and SQLite. Use for @DriftDatabase, Dart tables, .drift SQL files, DAOs, generated code, driftDatabase, NativeDatabase, WasmDatabase, reactive watch streams, joins, transactions, batches, upserts, indexes, foreign keys, schemaVersion, guided migrations, background isolates, read pools, WAL, web workers/OPFS/IndexedDB, backup/export, or slow and janky local database behavior across Android, iOS, Web, Windows, macOS, and Linux. Trigger when users mention Drift, Moor migration, database streams rerunning, database locked errors, N+1 local queries, large SQLite datasets, or broken Drift migrations. Do not use for generic remote API caching without Drift, Firebase/Firestore modeling, or server-only database design.

The file

Show its rules and text

We split this skill into the rules it gives an agent

32 rules. About 0 are common, 19 are distinctive, and 13 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈19

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 13

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Treat Drift as a typed SQLite persistence system with generated APIs, reactive invalidation, platform-specific executors, and irreversible …

    line 9
  2. Distinctive:

    Read only what the task requires:

    line 13
  3. Very specific:

    Every database constructor and owner.

    line 32
  4. Distinctive:

    Search for at least:

    line 37
  5. Distinctive:

    Do not add repositories, DAOs, converters, or isolate pools merely for symmetry.

    line 62
  6. Distinctive:

    Keep one long-lived logical database per application database file unless isolation is intentional.

    line 77
  7. Very specific:

    Use driftDatabase from drift_flutter for the ordinary cross-platform Flutter setup.

    line 79
  8. Very specific:

    Use NativeDatabase.createInBackground for manual native setup so synchronous SQLite work stays off the UI isolate.

    line 80
  9. Very specific:

    Use manual DriftIsolate wiring only when multiple application-controlled isolates must share one logical database.

    line 81
  10. Very specific:

    Use WasmDatabase.open or driftDatabase with DriftWebOptions on Web.

    line 82
  11. Distinctive:

    Avoid duplicate prefix indexes and low-selectivity leading columns unless a measured plan benefits.

    line 90
  12. Distinctive:

    Use joins or set-based SQL instead of query-per-row loops.

    line 91
  13. Very specific:

    Verify important plans with EXPLAIN QUERY PLAN and representative data before claiming an optimization.

    line 94
  14. Distinctive:

    Use a transaction for a business invariant spanning multiple statements.

    line 98
  15. Distinctive:

    Keep transactions short and free of network calls, UI waits, timers, and unrelated computation.

    line 99
  16. Very specific:

    Use batch/insertAll for bulk writes so Drift can reuse statements and reduce crossings.

    line 100
  17. Distinctive:

    Use explicit upserts only when the chosen primary/unique conflict target matches product semantics.

    line 101
  18. Very specific:

    Use get*() for one-shot reads and watch*() only when consumers need automatic updates.

    line 107
  19. Distinctive:

    Keep watched queries selective, indexed, and small.

    line 108
  20. Very specific:

    Do not combine an initial get() with watch().

    line 110
  21. Distinctive:

    Do not expose transaction-local streams outside their transaction.

    line 112
  22. Very specific:

    Prefer dart run drift_dev make-migrations and generated step-by-step migrations/tests over handwritten ad hoc upgrades.

    line 117
  23. Distinctive:

    Commit historical schema snapshots and generated migration support files required by the project.

    line 118
  24. Distinctive:

    Test upgrades from every supported historical version and assert retained business data, not only final DDL shape.

    line 119
  25. Very specific:

    Run complex migrations transactionally, handle foreign-key pragmas as documented, and check PRAGMA foreign_key_check.

    line 120
  26. Distinctive:

    Never use destructive fallback migration in production unless product policy explicitly permits data loss.

    line 121
  27. Very specific:

    Treat downgrades as unsupported unless a separately designed export/reinstall/restore flow exists.

    line 122
  28. Distinctive:

    Run code generation, formatting, analysis, and focused tests.

    line 142
  29. Distinctive:

    Run generated migration tests and data-integrity assertions.

    line 143
  30. Distinctive:

    Test create-new, upgrade, failed upgrade/rollback, reopen, and close ownership.

    line 144
  31. Distinctive:

    Test every shipped executor: native targets and real supported browsers.

    line 145
  32. Very specific:

    Verify Web worker and sqlite3.wasm deployment plus the selected persistence implementation.

    line 146

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Flutter Drift Persistence Engineering

Treat Drift as a typed SQLite persistence system with generated APIs, reactive invalidation, platform-specific executors, and irreversible schema evolution—not as a generic key-value store.

Reference routing

Read only what the task requires:

NeedReference
Dependencies, database declaration, connection ownership, DAOs, generated-code layoutreferences/01-setup-and-architecture.md
Tables, constraints, relationships, indexes, Dart/SQL queries, joins, paginationreferences/02-schema-and-queries.md
Writes, transactions, batches, streams, invalidation, profiling, large datasetsreferences/03-writes-streams-and-performance.md
Native isolates, driftDatabase, NativeDatabase, WAL/read pools, Web/WASMreferences/04-platforms-and-concurrency.md
Migrations, migration tests, ordinary tests, backup, diagnostics, security, reviewsreferences/05-migrations-testing-and-operations.md
Version baseline and authoritative URLsreferences/06-authoritative-sources.md

The bundled research was checked on 2026-08-11 against Drift 2.34.3, drift_flutter 0.3.1, and current official Drift/SQLite documentation. Always inspect the project’s pubspec.lock, SDK constraints, generated code, and installed package source before relying on a signature or default.

Inspect before changing code

Inspect:

  • pubspec.yaml, pubspec.lock, build.yaml, Flutter/Dart versions, and shipped platforms.
  • Database classes, tables, .drift files, DAOs, repositories, generated parts, and schema snapshots.
  • Every database constructor and owner; locate all opens, providers, overrides, and close() calls.
  • schemaVersion, migration, generated migration steps/tests, and historical schema files.
  • Hot queries, watched streams, indexes, write paths, transaction boundaries, and large result materializations.
  • Native/Web connection setup, worker/WASM assets, headers, database path, pragmas, isolate sharing, and backup behavior.

Search for at least:

@DriftDatabase @DriftAccessor Table TableIndex .drift
driftDatabase NativeDatabase WasmDatabase LazyDatabase DriftIsolate
schemaVersion MigrationStrategy make-migrations beforeOpen onUpgrade
watch watchSingle transaction batch insertAll readPool journal_mode
customSelect customStatement notifyUpdates close

Trace callers before fixing a symptom. Put connection ownership, migration policy, and repeated-query fixes at their shared boundary.

Define the persistence contract

Establish:

  1. Which data is authoritative, cached, replaceable, or user-created?
  2. Which platforms and browser modes must persist reliably?
  3. What consistency and atomicity must each workflow provide?
  4. Which queries are hot, how many rows can they touch, and how are they ordered?
  5. Which UI surfaces truly need reactive results?
  6. How will every released schema version migrate and retain data?
  7. Who owns database startup, lifetime, close, backup, restore, and destructive recovery?
  8. What data is sensitive, and what encryption or platform storage boundary is required?

Do not add repositories, DAOs, converters, or isolate pools merely for symmetry. Add a boundary when it owns queries, policy, test seams, or lifecycle.

Choose the smallest sound architecture

Default to:

UI/state
→ feature repository or DAO when it adds a real boundary
→ one application database
→ generated Drift API
→ platform-aware executor
→ SQLite/WASM persistence
  • Keep one long-lived logical database per application database file unless isolation is intentional.
  • Inject a QueryExecutor or DatabaseConnection for tests and specialized runtimes.
  • Use driftDatabase from drift_flutter for the ordinary cross-platform Flutter setup.
  • Use NativeDatabase.createInBackground for manual native setup so synchronous SQLite work stays off the UI isolate.
  • Use manual DriftIsolate wiring only when multiple application-controlled isolates must share one logical database.
  • Use WasmDatabase.open or driftDatabase with DriftWebOptions on Web; never use deprecated package:drift/web.dart for new work.
  • Close the database from its actual owner. Do not close a shared singleton from a widget.

Design schema from invariants and queries

  • Encode required values, uniqueness, keys, references, and safe defaults in the schema.
  • Enable SQLite foreign-key enforcement deliberately; declaring references does not replace runtime enforcement.
  • Index measured or clearly dominant WHERE, join, and ORDER BY paths. Match composite-index leftmost order to real predicates.
  • Avoid duplicate prefix indexes and low-selectivity leading columns unless a measured plan benefits.
  • Use joins or set-based SQL instead of query-per-row loops.
  • Select only needed columns for large or hot paths.
  • Give pagination a deterministic unique tie-breaker. Prefer keyset pagination for deep, frequently traversed datasets; use offset pagination for small/simple lists.
  • Verify important plans with EXPLAIN QUERY PLAN and representative data before claiming an optimization.

Keep writes atomic and bounded

  • Use a transaction for a business invariant spanning multiple statements; await every database operation inside it.
  • Keep transactions short and free of network calls, UI waits, timers, and unrelated computation.
  • Use batch/insertAll for bulk writes so Drift can reuse statements and reduce crossings.
  • Use explicit upserts only when the chosen primary/unique conflict target matches product semantics.
  • Require a deliberate where clause for updates and deletes unless the task explicitly targets the full table.
  • Chunk very large imports when a single transaction creates unacceptable memory, lock, or latency pressure; preserve restart/idempotency semantics.

Make reactivity intentional

  • Use get*() for one-shot reads and watch*() only when consumers need automatic updates.
  • Keep watched queries selective, indexed, and small. Drift invalidates by observed tables, so a write may rerun more streams than the exact changed rows require.
  • Share or hoist equivalent streams when UI rebuilds would create redundant subscriptions.
  • Do not combine an initial get() with watch(); Drift streams emit an initial current snapshot.
  • Declare readsFrom/update metadata correctly for raw custom SQL and external writes, or reactive results can become stale.
  • Do not expose transaction-local streams outside their transaction.

Evolve schemas without gambling with data

  • Bump schemaVersion for every released schema change.
  • Prefer dart run drift_dev make-migrations and generated step-by-step migrations/tests over handwritten ad hoc upgrades.
  • Commit historical schema snapshots and generated migration support files required by the project.
  • Test upgrades from every supported historical version and assert retained business data, not only final DDL shape.
  • Run complex migrations transactionally, handle foreign-key pragmas as documented, and check PRAGMA foreign_key_check.
  • Never use destructive fallback migration in production unless product policy explicitly permits data loss.
  • Treat downgrades as unsupported unless a separately designed export/reinstall/restore flow exists.

Optimize from evidence

Classify the bottleneck:

startup jank → executor placement, eager queries, migration/open work
slow query → scan/sort/join plan, missing or wrong index, excess columns/rows
database locked → multiple connections, long writes, WAL/read-pool configuration
stream churn → broad table invalidation, duplicate subscriptions, expensive result set
slow import → per-row statements, oversized transaction, serialization/isolate overhead
web slowness → chosen storage backend, worker deployment, missing COOP/COEP capabilities
memory growth → unbounded get(), retained result lists/subscriptions, large blobs

Profile release/profile builds with representative row counts. Background isolates protect UI responsiveness but add message-copying and coordination overhead; they do not make SQL intrinsically faster.

Validate before finishing

  • Run code generation, formatting, analysis, and focused tests.
  • Run generated migration tests and data-integrity assertions.
  • Test create-new, upgrade, failed upgrade/rollback, reopen, and close ownership.
  • Test every shipped executor: native targets and real supported browsers.
  • Verify Web worker and sqlite3.wasm deployment plus the selected persistence implementation.
  • Exercise cancellation/retry around imports, exports, backups, and app lifecycle.
  • Benchmark hot queries and bulk writes with production-like data; inspect query plans.
  • Review the final diff for unbounded reads, N+1 loops, missing constraints/indexes, broad watched queries, leaked connections, sensitive SQL logging, and data-loss fallbacks.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.