dbinspect_drift
A Drift adapter for
dbinspect_bridge, so a desktop
client can browse your app's database while it runs.
import 'package:dbinspect_bridge/dbinspect_bridge.dart';
import 'package:dbinspect_drift/dbinspect_drift.dart';
await startBridge(
appName: 'example_app',
adapters: [
DriftAdapter(database: db, id: 'app', displayName: 'app.db'),
],
);
Stores come from allTables, the schema from the generated $columns, and
paging from LIMIT/OFFSET through customSelect. Blobs are truncated at
64 KB.
What this gives you that opening the file does not
A Drift database is a SQLite file, and a client can open one directly with no help from your app. Three things only exist while your application is running, and they are the reason this adapter exists:
- the declared schema —
inferred: false, compiled in from your Dart table classes rather than read back fromPRAGMA table_info; - Drift's type mapping — a
DateTimeColumnis aninton disk and a date here, aBoolColumnis0/1on disk and a boolean here; - the change feed, with the honest caveat below.
Column names on the wire are the SQL names drift generated, not the Dart
ones: joinedAt in your table class is joined_at here, in sortBy, in
values and in every row.
Watching is in-process only, and the client is told so
capabilities.watch is true and watchScope is "in-process".
tableUpdates() is an in-process event bus fed by notifyUpdates, not a
database trigger. It reports writes made through this Drift instance and
nothing else — a second isolate, another process, or a native writer produces no
event at all. The client labels the toggle Live (this app's writes only)
rather than Live, because the developer debugging exactly that cross-process
case is the one an unqualified badge would mislead.
There is a test for the limit itself: two Drift instances over one SQLite handle, a write through the second, and no event on the first.
Writes made through the bridge do fire it — customInsert and customUpdate
are called with updates:, so the bridge is not the one writer tableUpdates
never reports.
Events are tableChanged: a Set<TableUpdate> is per table, not per row, and
flattening it into insert would be inventing information the bus does not
carry.
Reading only, unless you say otherwise
sql and edit are false by default, so the adapter browses and pages and
does nothing else.
DriftAdapter(database: db, id: 'app', displayName: 'app.db',
allowRawQuery: true, allowWrite: true);
allowRawQuery opens SELECT, WITH … SELECT and EXPLAIN. allowWrite
opens the row editor and, in the console, INSERT/UPDATE/DELETE/REPLACE.
ATTACH, DETACH, PRAGMA, VACUUM, extension loading, DDL and a second
statement after a ; are refused whatever you turn on — the gate is an
allow-list over a real tokenizer in dbinspect_bridge, not a keyword scan.
Row identity is the table's single-column primary key. A table with no primary
key, or with a composite one, refuses updateRow and deleteRow naming which
case it is; insertRow still works on both.
Drift version floor, measured
| Version | How it was established | |
|---|---|---|
| Analyzes clean from | 2.12.0 | dart analyze lib against 2.12.0, 2.16.0, 2.20.0 and 2.34.3, all clean. Below 2.12 drift's own sqlite3 constraint moves and the package stops resolving at all. |
| Suite runs green at | 2.34.3 | dart test, 2026-08-08 |
The two numbers differ for one reason worth knowing: test/fixture_database.g.dart
is generated code, and drift's generated output is shaped by the version that
produced it — the committed fixture does not compile on 2.20. That is drift's
compatibility story for generated code, not a limit of this adapter, and it is
why the tested floor cannot simply be lowered to the analyzed one.
drift_dev and build_runner are deliberately not dev dependencies here; see
the note in pubspec.yaml for how to regenerate the fixture.
Licence
Apache-2.0.
Libraries
- dbinspect_drift
- A Drift adapter for
dbinspect_bridge.