zero_inspector_kit 1.9.1
zero_inspector_kit: ^1.9.1 copied to clipboard
One-line in-app developer console for Flutter — HTTP/WebSocket/gRPC inspection, logs, database, memory leaks, FPS jank, routes, alerts, bug reports. Auto-disabled in release.
Zero Inspector Kit #
English | 简体中文
An in-app developer console for Flutter: inspect HTTP, WebSocket & gRPC traffic, monitor logs, database, memory leaks and FPS in real time, track routes, get alerts and one-tap bug reports — all without leaving your app. Auto-disabled in release.
🔔 Upgrade recommended: This release trims the published package — local build artifacts are no longer shipped, cutting the downloaded archive from ~23 MB to ~3 MB. No runtime changes. All users are encouraged to upgrade to the latest version (
^1.9.1).
🌐 Official Website · 📦 View on pub.flutter-io.cn · 🔗 View on GitHub
Table of Contents #
Features #
- Zero-Invasion Integration — One line of code, no changes to existing project code.
- Network Inspector — Real-time capture of all HTTP (http & Dio) requests; modify bodies/headers via interceptor rules; batch cURL copy; sensitive-header masking; filterable by method/status/interception.
- WebSocket / gRPC Capture — Opt-in streaming-protocol capture (off by default, runtime toggle like Memory/FPS); WebSocket frames and gRPC calls appear in the Network list.
- Logging System — Auto-captures
print(),debugPrint(), and custom logs across multiple levels; integrates with third-party log libraries; auto-scroll (pausable), regex search, tag filtering and one-tap copy of a single log entry. - Error Monitor — Dedicated Errors tab (since v1.9.0): hooks
FlutterError.onError+runZonedGuarded, aggregates & dedups crashes by type + stack signature with count and first/last seen; a red count badge sits on the Errors tab icon. - Session Persistence — Logs / network / errors are asynchronously flushed into the inspector's own
zero_inspector_kit.db(since v1.9.0; a disk ring buffer that also shows up in the Database tab); on launch logs & errors replay into their tabs; export & share the full session archive from the panel header. - Database Viewer — Inspect SQLite and other databases via custom providers.
- Memory Monitor — Trend chart, Dart Heap, Native memory breakdown, leak detection, image-cache & storage stats (master switch to avoid overhead). Since v1.9.0 the leak detector also bridges Flutter's official
FlutterMemoryAllocationsto cut false positives. - FPS Monitor — Real-time FPS, jank detection, trend chart, frame records (master switch to avoid overhead).
- Route Tracker — Navigation history and current route.
- Alert System — Rule-based alerts on network/logs/memory/FPS with unread badge and throttling.
- Floating Button — Breathing-animation overlay button that auto-docks to screen edges, avoiding back-gesture conflicts.
- One-Click Bug Report — Tap the bug icon in the panel header to share a ready-to-file snapshot (device model + OS + current memory + recent logs + recent network) via the system share sheet.
- Modern UI — Dark theme with gradients and centralized, customizable colors.
- Cross-Platform — Android and iOS.
Screenshots #
Click any thumbnail to open the full-size image.
| Network Inspector | Database Viewer |
|---|---|
![]() |
![]() |
| Logging | Memory Monitor |
|---|---|
![]() |
![]() |
| FPS Monitor | Route Tracker |
|---|---|
![]() |
![]() |
| Alerts | Widget Inspector |
|---|---|
![]() |
![]() |
Installation #
Pub.dev (Recommended) #
dependencies:
zero_inspector_kit: ^1.9.1
GitHub #
dependencies:
zero_inspector_kit:
git:
url: https://github.com/zero-labsco/zero_inspector_kit.git
ref: release/v1.9.1 # replace 1.9.1 with the version you need
Usage #
Zero-Invasion Integration (Recommended) #
Integrate with just 1 line of code, no need to modify any existing project code:
import 'package:flutter/material.dart';
import 'package:zero_inspector_kit/zero_inspector_kit.dart';
void main() {
// Single line: init inspector, capture print() via Zone, show floating button
ZeroInspectorKit.runAppWithInspector(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorObservers: [InspectorRouteObserver()],
home: Scaffold(
appBar: AppBar(title: const Text('App')),
body: const Center(child: Text('Hello World')),
),
);
}
}
What the inspector does automatically (no other code changes):
| Capability | How |
|---|---|
| ✅ Log Capture | All print() / debugPrint() calls and Flutter errors via Zone |
| ✅ Network Interception | All http & Dio requests via HttpOverrides (Dio uses HttpClient) |
| ✅ Database Scan | Auto-scans and registers SQLite databases |
| ✅ Floating Button | Shown via Overlay, no manual widget needed |
| ✅ Route Tracking | Via InspectorRouteObserver (auto-injected into MaterialApp) |
Production Build: The inspector is automatically disabled in release mode — tree-shaking removes all related code, so you never need to delete anything.
Manual Integration (More Control) #
import 'package:shared_preferences/shared_preferences.dart';
import 'package:hive/hive.dart';
void main() async {
// 1) Manual init (more control than the one-line helper)
ZeroInspectorKit.init(
enableWidgetInspector: true, // optional: pre-enable Widget Inspector
enableNetworkTimeline: true, // optional: pre-enable Network Timeline
);
// 2) Register custom data sources (one-line API)
final prefs = await SharedPreferences.getInstance();
ZeroInspectorKit.registerSharedPrefs(SharedPreferencesAdapter(prefs));
final settings = await Hive.openBox('settings');
final cache = await Hive.openBox('cache');
ZeroInspectorKit.registerHive({
'settings': HiveBoxAdapter(settings),
'cache': HiveBoxAdapter(cache),
});
// 3) Wrap your app
runApp(ZeroInspectorKit.wrapApp(const MyApp()));
}
About dependencies: the plugin itself doesn't need
shared_preferences/hive; your app must still add them to its ownpubspec.yamlif it wants to inspect these. Both appear under the Database tab and reuse the same browse/export flow as SQLite.
Prefer the raw API? Register the providers yourself.
import 'package:zero_inspector_kit/zero_inspector_kit.dart';
void main() {
ZeroInspectorKit.init();
DatabaseRegistry.instance.registerProvider(SharedPrefsProvider(prefs: prefs));
DatabaseRegistry.instance.registerProvider(HiveProvider(box: box, name: 'settings'));
runApp(ZeroInspectorKit.wrapApp(const MyApp()));
}
Logging #
Start automatic capture from multiple sources:
InspectorLogInterceptor.instance.start();
Auto-captured: print() / debugPrint(), Flutter framework errors (FlutterError.onError hook), and runZonedGuarded exceptions.
Since v1.9.0, those errors are also aggregated & deduplicated in the dedicated Errors tab (see below) — the log stream keeps them as error-level lines, while Errors answers "is the same crash repeating?".
Manual logging (optional):
InspectorLog.v('Verbose'); InspectorLog.d('Debug');
InspectorLog.i('Info'); InspectorLog.w('Warning');
InspectorLog.e('Error');
Third-party libraries that log via print()/debugPrint() (e.g. logger, flutter_logger) are captured automatically — no config needed. Use onLogCaptured to sync captured logs back into your own logging service:
import 'package:logger/logger.dart';
final logger = Logger();
InspectorLogInterceptor.instance.onLogCaptured = (entry) {
logger.log(_mapLogLevel(entry.level),
'${entry.tag != null ? '[${entry.tag}] ' : ''}${entry.message}');
};
Error Monitor #
Available since v1.9.0
The Errors tab answers "is the same crash happening repeatedly?" ErrorService hooks FlutterError.onError (keeping the default red-screen behavior) plus runZonedGuarded inside runAppWithInspector(), then aggregates each exception by type + stack signature: repeated crashes merge into one record with a ×N count and first/last-seen time. Tap a row to expand the full stack sample, filter by search, and copy individual stacks. A red count badge on the Errors tab icon shows the number of aggregated records while the panel is open.
import 'package:zero_inspector_kit/zero_inspector_kit.dart';
// Manual report — gRPC / custom protocol / your own error paths
ErrorService.instance.report(error, stackTrace, 'myModule');
// Read aggregated records (newest first)
final ErrorRecord latest = ErrorService.instance.errors.first;
// Clear all records
ErrorService.instance.clear();
Toggle with enableErrorCapture in init() (default true). Full details on the Errors page.
Session Persistence #
Available since v1.9.0
Logs, network requests, and aggregated errors are asynchronously flushed into the inspector's own database file, zero_inspector_kit.db — a SQLite-backed disk ring buffer that also appears in the Database tab. On the next launch, logs and aggregated errors replay into their tabs; network requests stay archived on disk for later export. Data therefore survives app restarts even if you never opened the panel. Tap the storage icon in the panel header to open the Persisted data manager: see row counts per category, export the full session archive as JSON (share sheet), or clear the disk.
// Reading persisted data programmatically (optional)
final logs = await PersistenceService.instance.loadLogs();
final errors = await PersistenceService.instance.loadErrors();
// Full-session snapshot, e.g. attach to a bug report
final String archive = await PersistenceService.instance.buildSessionArchiveJson();
// Export & share via the system share sheet
await PersistenceService.instance.exportSessionArchiveAndShare();
// Wipe the disk ring buffer
await PersistenceService.instance.clearAll();
Toggle with enablePersistence in init() (default true). Set both enableErrorCapture and enablePersistence to false to keep everything in memory only.
Network Requests #
All HTTP requests (both http and Dio) are intercepted via HttpOverrides automatically after init — no setup required.
import 'package:http/http.dart' as http;
final r = await http.get(Uri.parse('https://api.example.com/data')); // captured
import 'package:dio/dio.dart';
final dio = Dio();
final r = await dio.get('https://api.example.com/data'); // captured
Note: Dio uses
IOHttpClientAdapter(which usesdart:io'sHttpClient), so it is captured automatically without extra config.
WebSocket / gRPC Capture (Off by Default) #
WebSocket, gRPC, and other streaming protocols are captured opt-in — off by default and toggled at runtime like the Memory/FPS monitors, so apps that don't use them pay nothing.
WebSocket: replace WebSocket.connect with InspectorWebSocket.connect. Enable capture first by tapping the WS switch in the Network tab.
import 'dart:io';
import 'package:zero_inspector_kit/zero_inspector_kit.dart';
final ws = await InspectorWebSocket.connect('wss://example.com/socket');
ws.listen((message) {
// incoming frames are auto-captured while capture is enabled
});
ws.add('ping'); // outgoing frames are auto-captured too
Captured frames appear in the Network list (method WS), reusing the timeline & detail view. When capture is off, InspectorWebSocket.connect behaves exactly like WebSocket.connect with zero overhead.
gRPC / web_socket_channel / other stacks: these can't be transparently intercepted by dart:io, so use the manual hook:
WsInspectorService.instance.recordCall(
name: 'UserService/GetUser',
request: requestJson,
response: responseJson,
);
Network Request Interceptor #
Modify requests via rules — useful for testing parameters without touching app code.
Workflow: send a request → open detail → tap the interceptor icon → configure rule (URL pattern, method, body/header edits) → save. Subsequent matching requests use the modified parameters.
| Aspect | Detail |
|---|---|
| Supported edits | Request body & headers (POST/PUT/PATCH only) |
| GET requests | View-only, cannot be modified (no body) |
| Rule matching | Exact or regex URL pattern; method filter (GET/POST/PUT/DELETE/PATCH/HEAD/Any) |
When no rules are configured or they are disabled, all requests are sent unmodified.
Database Provider #
DatabaseRegistry.instance.registerProvider(SqliteDatabaseProvider());
Memory Monitor #
Comprehensive analysis with a master switch (off by default to avoid overhead).
- Master Switch: top toggle in the Memory panel; off = no timers / no VM Service connection.
- Trend Chart: 2-minute window (240 snapshots × 500ms), switchable across Process RSS / Dart Heap / New Space / Old Space.
- Dart Heap (needs VM Service): usage/capacity/external bars; new/old-space breakdown; manual GC trigger.
- Native Memory (real devices): Android PSS breakdown; iOS physical footprint/compressed/RSS; low-memory warning.
- Leak Detection (Dart 2.17+
WeakReference):trackObject()four-state flow; auto-GC verification; UI shows suspected/tracking/released objects. Since v1.9.0 it also bridges Flutter's officialFlutterMemoryAllocations(enabled viaenableFlutterLeakTracker) so objects already reporteddisposedare treated as released — cutting false positives.
myBloc.trackMemoryLeak(tag: 'HomePage_myBloc'); // shorthand
// or: trackMemoryLeak(myBloc, tag: 'HomePage_myBloc');
myBloc.untrackMemoryLeak(); // cancel
MemoryInspectorService.instance.clearLeakRecords(); // clear all
- Image Cache: live size/count, pending vs live, usage bar, one-click clear.
- App Storage: documents / temp / DB size, one-click temp clear.
⚠️ VM Service availability: when debugging via
flutter runon PC, Dart VM Heap may showVM: OFF(port-forwarding quirk). Native memory and process RSS still work. Opening the debug app directly on-device shows Heap data correctly.
FPS Monitor #
Real-time frame analysis with a master switch (off by default).
- Master Switch: top toggle; off = no timings callback, zero overhead.
- Metrics: current FPS, jank rate, total frames, 30-second trend chart (60 points), janky-frame list (>16ms).
- Accuracy (since v1.2.1): uses real
buildStarttimestamps (notDateTime.now()) andrasterFinish - buildStartduration, catching GPU raster jank.
FpsService.instance.start();
final fps = FpsService.instance.currentFps;
final jank = FpsService.instance.jankRate;
FpsService.instance.clear();
Custom Database Provider #
Implement DatabaseProvider for other databases:
class MyCustomDatabaseProvider implements DatabaseProvider {
@override
String get name => 'CustomDB';
@override
Future<List<DatabaseInfo>> getDatabases() async => [];
@override
Future<QueryResult> queryTable(String dbPath, String tableName, {int limit = 50}) async =>
QueryResult(columns: [], rows: []);
}
DatabaseRegistry.instance.registerProvider(MyCustomDatabaseProvider());
Widget Inspector & Network Timeline (on by default) #
Both are on by default. You can toggle them off via the panel switch if you don't need them. Pre-enable at startup:
ZeroInspectorKit.runAppWithInspector(
const MyApp(),
enableWidgetInspector: true, // one-shot tree snapshot + breadcrumb navigation
enableNetworkTimeline: true, // live request waterfall
);
- Widget Inspector: one-shot snapshot browsed via breadcrumb navigation (not live; tap Refresh to re-snapshot).
- Network Timeline: live waterfall of requests, no manual refresh.
Bug Report #
Tap the bug icon in the panel header to generate and share a bug report in one tap — ideal for QA to attach environment context when filing issues.
The shared text snapshot includes:
- Device: real model (e.g.
Pixel 8 Pro/iPhone (iPhone16,1)), OS & version, locale, Dart runtime, CPU cores. - Memory: current heap usage and whether Native memory is supported.
- Recent logs: the latest captured log entries.
- Recent network: the latest captured requests.
Sensitive headers are masked the same way as in the Network tab (toggle "Sensitive hidden" in the panel). No data leaves the device except through the share target you choose (mail, IM, etc.).
Requires no extra setup — it works as soon as the inspector is running.
API Reference #
FloatingInspectorButton #
| Parameter | Type | Description |
|---|---|---|
enabled |
bool |
Whether the inspector is enabled (default: true, auto-disabled in release) |
ConditionalInspector #
Auto shows/hides the inspector based on build mode.
ConditionalInspector(child: YourAppWidget())
| Parameter | Type | Description |
|---|---|---|
child |
Widget |
The child widget |
enabled |
bool |
Whether the inspector is enabled (default: true) |
InspectorLogInterceptor #
| Method | Description |
|---|---|
start() / stop() |
Start / stop capturing logs |
log(level, message, tag) |
Add a log entry |
verbose/debug/info/warning/error(message, tag) |
Add a log at the given level |
| Property | Type | Description |
|---|---|---|
onLogCaptured |
void Function(LogEntry)? |
Callback for third-party log integration |
InspectorLog #
Shorthand wrapper around InspectorLogInterceptor.instance.
| Method | Description |
|---|---|
start() / stop() |
Start / stop capturing logs |
log(level, message, {tag}) |
Add a log entry |
v/d/i/w/e(message, {tag}) |
Add a log at the given level |
| Property | Type | Description |
|---|---|---|
isRunning |
bool |
Whether log capture is active |
InspectorRouteObserver #
Navigator observer for tracking route changes.
FpsService #
Singleton FPS service extending ChangeNotifier.
| Method | Description |
|---|---|
start() / stop() |
Start / stop monitoring |
clear() |
Clear all history and counters |
| Property | Type | Description |
|---|---|---|
isRunning |
bool |
Whether monitoring is active |
currentFps |
double |
Current FPS (every 500ms) |
jankRate |
double |
Jank rate (%) |
totalFrameCount |
int |
Total frames captured |
totalJankyCount |
int |
Total janky frames (>16ms) |
lastFrameJanky |
bool |
Whether the latest frame was janky |
fpsHistory |
List<double> |
Recent 60 FPS values (unmodifiable) |
frameRecords |
List<FrameRecord> |
Recent frame records (unmodifiable, ≤3600) |
runInspectorApp #
Runs your app inside the inspector Zone for automatic print() capture.
runInspectorApp(VoidCallback appRunner)
| Parameter | Type | Description |
|---|---|---|
appRunner |
VoidCallback |
Function to run your app (usually runApp) |
Contributing #
Contributions are welcome! Please read the Contributing Guidelines before submitting issues or pull requests.
- 🐛 Report a Bug
- 💡 Request a Feature
- 💬 Join Discussions
- 📖 Contributing Guide
License #
This project is licensed under the GNU General Public License v3.0 — see the LICENSE file for details.
This plugin is licensed under GPL-3.0, which permits commercial use. Any derivative project that modifies this plugin and redistributes it must publish its complete source code under the same license.
This plugin is provided "as is", without warranty of any kind. The author assumes no responsibility or liability for the functionality, security, or any consequences arising from the use of modified versions or derivative projects.







