A Flutter Database Inspector Framework for inspecting SQLite and SharedPreferences directly on device β no adb, no external tools, no laptop needed.
Designed for QA and developers. Works out of the box (
DbLens.open()), or compose your own UI from headless controllers and reusable widgets. Hidden in release builds (kReleaseMode) unless you opt in withDbLensConfig.allowInRelease(e.g. QA builds).
| SQLite Browser | Pagination | SharedPreferences |
|---|---|---|
![]() |
![]() |
![]() |
dev_dependencies:
db_lens: ^1.2.0import 'package:db_lens/db_lens.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite/sqflite.dart';
// SQLite
final db = await openDatabase('my_app.db');
DbLens.register('Main DB', db);
// SharedPreferences
final prefs = await SharedPreferences.getInstance();
DbLens.registerSharedPreferences('App Prefs', prefs);
// Open the built-in inspector (bottom sheet by default)
DbLens.open(context);
// Or the full-page database browser
DbLens.openBrowser(context);DbLens.openBrowser(context) opens a full-page browser: source list, expandable row cards, cell editing, and a raw SQL console.
DbLens.openBrowser(context);
// QA / release builds: opt in explicitly (hidden in release by default)
DbLens.openBrowser(context, allowInRelease: true);- Delete a SharedPreferences key β trash icon on the row card, with a confirmation dialog. SQLite sources don't expose row deletion.
- SQL console β SQLite only. Tap a table chip to insert its name at the cursor. A trailing
;is fine. - The browser uses a fixed palette;
DbLensThemeDataapplies toDbLens.open/ the panel, not to the browser. - Custom sources:
LensDataSource.supportsRowDelete/deleteRowlet your own source opt in to deletion.
Drop it anywhere β app bar, drawer, debug menu, settings page. Automatically hidden in release builds unless config: DbLensConfig(allowInRelease: true).
DbLensButton()
DbLensButton(
label: 'Inspect Data',
icon: Icons.bug_report,
style: ElevatedButton.styleFrom(backgroundColor: Colors.teal),
)DbLens.open() supports two navigation modes via DbLensConfig.presentationMode:
// Bottom sheet (default)
DbLens.open(context);
// Full-screen route
DbLens.open(
context,
config: const DbLensConfig(
presentationMode: DbLensPresentationMode.fullPage,
fullscreenDialog: true,
),
);To embed the inspector directly in your widget tree (e.g. a debug tab), use DbLens.buildPanel() instead β no navigation involved:
Scaffold(
body: DbLens.buildPanel(
theme: DbLensThemeData.fromMaterialTheme(Theme.of(context)),
),
)For full control, compose your own inspector from public widgets and sub-controllers:
DbLensControllerScope(
theme: DbLensThemeData.fromMaterialTheme(Theme.of(context)),
child: DbLensLayout(
sidebar: const DbLensSourceList(),
toolbar: const DbLensToolbar(actions: [DbLensRefreshAction()]),
body: Builder(
builder: (context) {
final c = DbLensControllerScope.of(context);
return AnimatedBuilder(
animation: c.table,
builder: (context, _) {
final columns = c.activeColumns;
return DbLensTableView(
rows: c.visibleRows(columns: columns),
columns: columns,
);
},
);
},
),
),
)DbLensController is split into focused sub-controllers β use only what you need:
| Sub-controller | Responsibility |
|---|---|
controller.source |
Sources, collections, selection |
controller.table |
Rows, search, pagination |
controller.query |
Raw SQL mode |
controller.queryHistory |
Per-session SQL history |
controller.edit |
Cell editing |
controller.browse |
Browse-all-sources snapshot |
See doc/custom_ui.md for the full composition guide, widget reference, direct data access API, and change history integration.
DbLens.open(
context,
theme: DbLensThemeData.fromMaterialTheme(Theme.of(context)),
);
// Or custom tokens
DbLens.open(
context,
theme: const DbLensThemeData(accent: Colors.teal),
);DbLens.register('Main DB', mainDb);
DbLens.register('Cache DB', cacheDb);
DbLens.registerSharedPreferences('App Prefs', prefs);Switch between sources inside the panel. Search and filter source and collection lists in real time.
Track insert/update/delete changes across registered collections (debug builds only).
Off by default. The tracker polls every row of every collection on a timer, which is expensive. Turn it on only when you need it, ideally before registering sources.
Compose the header and panel separately β the header owns navigation, search/filter toggles, tracking, and clear; the panel owns the entry list.
DbLens.configureHistory(
enabled: true,
pollInterval: const Duration(seconds: 5),
);
final historyController = DbLens.createHistoryController();
await historyController.loadFor(sourceId);
// Modal bottom sheet (all tables)
await DbLensHistorySheet.show(
context,
controller: historyController,
sourceName: 'Main DB',
);
// Modal bottom sheet scoped to one table
await DbLensHistorySheet.show(
context,
controller: historyController,
sourceName: 'Main DB',
table: 'users',
);
// Full-page route
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Scaffold(
body: Column(
children: [
DbLensHistoryHeader(
controller: historyController,
sourceName: 'Main DB',
onBack: () => Navigator.pop(context),
),
Expanded(
child: DbLensHistoryPanel(
controller: historyController,
table: 'users', // optional β scope to one collection
),
),
],
),
),
),
);
historyController.dispose();| Widget | Role |
|---|---|
DbLensHistoryHeader |
Title, optional back, search/filter toggles, tracking, clear |
DbLensHistoryPanel |
Entry list; optional table scope |
DbLensHistorySheet |
Bottom-sheet wrapper around header + panel |
DbLensHistoryEntryView |
Entry detail with diff or raw JSON + copy |
| ποΈ | SQLite table browser with pagination |
| π | SharedPreferences inspector (key, type, value) |
| π | Search rows across all columns; searchable source & collection selectors |
| π | Pagination (configurable via DbLensConfig.pageSize, default 10) |
| π οΈ | Raw SQL query (SQLite); auto-select table on simple SELECT |
| π¨ | Table / JSON view toggle β current page as pretty-printed JSON array |
| π | Tap row β JSON bottom sheet; long-press β copy or edit cell |
| βοΈ | Edit cell values (SQLite UPDATE / SharedPreferences set*) |
| π€ | Copy all rows as JSON |
| ποΈ | Delete SharedPreferences keys (browser row card, DbLensRowJsonSheet.onDelete) |
| π§ | Full-page browser via DbLens.openBrowser with table chips in the SQL console |
| π | Change history with diff view, filters, and copy JSON (polling-based, opt-in) |
| π§© | Headless controllers + reusable widget library for custom UI |
| π¨ | DbLensThemeData β customizable panel colors |
| π | Refresh on demand |
| πΎ | Multiple source support |
| π | No-op in release builds unless allowInRelease |
DbLens.open(
context,
config: const DbLensConfig(
pageSize: 20,
enablePrefetch: true,
historyPollInterval: Duration(seconds: 5),
allowInRelease: false,
presentationMode: DbLensPresentationMode.bottomSheet,
),
);Run the included example to try every integration pattern:
cd example
flutter runDemos include bottom sheet, full page, embedded panel, custom UI composition, and change history. See example/README.md.
| 0.0.x | 1.0.0 |
|---|---|
DbLens.openPage() |
DbLens.open(context, config: DbLensConfig(presentationMode: fullPage)) |
DbLens.openCustom() |
DbLensControllerScope + compose widgets yourself |
DbLensInspectorScope |
DbLensControllerScope |
DbLensPresentationMode.embedded |
DbLens.buildPanel() |
controller.sources |
controller.source.sources |
controller.searchText |
controller.table.searchText |
controller.pagination |
controller.table.pagination |
See LICENSE.


