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 schemainferred: false, compiled in from your Dart table classes rather than read back from PRAGMA table_info;
  • Drift's type mapping — a DateTimeColumn is an int on disk and a date here, a BoolColumn is 0/1 on 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.