<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jaimin Raval</title>
    <description>The latest articles on DEV Community by Jaimin Raval (@jaimin_raval_f70ffdbbf594).</description>
    <link>https://dev.to/jaimin_raval_f70ffdbbf594</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3939646%2Fa2b81ce3-5ead-4b6a-9d8e-a71f1fb84012.jpg</url>
      <title>DEV Community: Jaimin Raval</title>
      <link>https://dev.to/jaimin_raval_f70ffdbbf594</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jaimin_raval_f70ffdbbf594"/>
    <language>en</language>
    <item>
      <title>I Built an All-in-One Debug Overlay for Flutter That Replaces 6 Separate Tools</title>
      <dc:creator>Jaimin Raval</dc:creator>
      <pubDate>Tue, 02 Jun 2026 12:48:49 +0000</pubDate>
      <link>https://dev.to/jaimin_raval_f70ffdbbf594/i-built-an-all-in-one-debug-overlay-for-flutter-that-replaces-6-separate-tools-5a5i</link>
      <guid>https://dev.to/jaimin_raval_f70ffdbbf594/i-built-an-all-in-one-debug-overlay-for-flutter-that-replaces-6-separate-tools-5a5i</guid>
      <description>&lt;p&gt;&lt;em&gt;No more switching between Proxyman, print statements, and prayer — everything your QA and dev team needs, directly inside the app.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Every Flutter developer has lived this cycle.&lt;/p&gt;

&lt;p&gt;You ship a build to QA. They come back: &lt;em&gt;"The user list isn't loading."&lt;/em&gt; You ask for a screenshot. They send a screenshot of the empty screen. You ask what API call failed. They don't know. You ask what device it was on. They send you an Android model number from 2019 that you've never heard of.&lt;/p&gt;

&lt;p&gt;So you add a bunch of &lt;code&gt;print()&lt;/code&gt; statements, rebuild, and hand them another build. Repeat this three times, burn half a day, and finally discover the issue was a 401 because a token wasn't being passed in the header.&lt;/p&gt;

&lt;p&gt;There had to be a better way.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pub.dev/packages/flutter_blackbox" rel="noopener noreferrer"&gt;&lt;strong&gt;flutter_blackbox&lt;/strong&gt;&lt;/a&gt; is an all-in-one in-app debug and QA overlay for Flutter. Shake the device (or tap a floating button, or press F12 on desktop) — a full debug panel slides up, directly inside your running app, with no external proxy or tooling required.&lt;/p&gt;

&lt;p&gt;Here's what's inside:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Panel&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🌐 &lt;strong&gt;Network&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Every HTTP request, live. Headers, body, status, timing bars, cURL export, HAR export. Swipe to dismiss.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🎛 &lt;strong&gt;Mocking&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Intercept any API call and replace it with a local JSON response. Throttle slider to simulate slow networks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📋 &lt;strong&gt;Logs&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Auto-captures every &lt;code&gt;debugPrint()&lt;/code&gt; and &lt;code&gt;print()&lt;/code&gt;. Filterable by level, searchable by tag.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚡ &lt;strong&gt;Performance&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Live FPS graph, jank detection, frame budget visualization.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔄 &lt;strong&gt;Rebuilds&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Tracks widget rebuild counts in real time. Heat-colored ranking. Auto mode needs zero code changes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💾 &lt;strong&gt;Storage&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Inspect, edit, delete key-value pairs from SharedPreferences, GetStorage, Hive, or any backend. Sensitive keys auto-redacted.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔌 &lt;strong&gt;Socket IO&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Auto-captures all incoming Socket.IO events. Zero changes to your socket code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📱 &lt;strong&gt;Device&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Platform, OS version, screen metrics, pixel ratio, connectivity status — in glassmorphic cards.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐛 &lt;strong&gt;Crashes&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Auto-catches Flutter framework errors, async exceptions, and unhandled platform errors.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🗺️ &lt;strong&gt;Journey&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Records every route change and tap so you can reconstruct exactly what a user did before a crash.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📝 &lt;strong&gt;QA Reports&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;One-tap Markdown report: screenshot + device info + journey + failed requests + stack traces. Paste directly into Jira or GitHub Issues.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;📊 &lt;strong&gt;Mini HUD&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Drag the floating button to the bottom edge for a persistent live bar showing FPS, HTTP status counts, and crash count.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All of this compiles out of release builds via &lt;code&gt;kDebugMode&lt;/code&gt;. Zero runtime cost in production. Zero optional dependencies — the package is 63 KB.&lt;/p&gt;




&lt;h2&gt;
  
  
  Zero Runtime Cost, Zero Bloat
&lt;/h2&gt;

&lt;p&gt;This was the hardest constraint to get right.&lt;/p&gt;

&lt;p&gt;Most debug tools add their dependencies to your production app. If your tool depends on &lt;code&gt;dio&lt;/code&gt;, every user of your app is carrying that dependency whether they use the debug panel or not. That's not acceptable.&lt;/p&gt;

&lt;p&gt;BlackBox solves this with a &lt;strong&gt;CLI-generated adapter architecture&lt;/strong&gt; — the same pattern used by &lt;code&gt;build_runner&lt;/code&gt;, &lt;code&gt;freezed&lt;/code&gt;, and &lt;code&gt;injectable&lt;/code&gt;. The package itself ships with zero optional dependencies. When you add it to your project, you run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dart run flutter_blackbox:init &lt;span class="nt"&gt;--generate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI reads your &lt;code&gt;pubspec.yaml&lt;/code&gt;, detects which libraries you actually use (Dio, http, Socket.IO, SharedPreferences), and generates a &lt;code&gt;lib/blackbox_adapters.dart&lt;/code&gt; file containing only the adapters your project needs. Dio users don't get &lt;code&gt;http&lt;/code&gt; installed. Socket.IO users don't get SharedPreferences adapters. You get exactly what you use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🐞 BlackBox Init — Auto-detecting your dependencies...

✅ Found dio          → Dio HTTP client
⬚  http              → not found, skipping
⬚  socket_io_client  → not found, skipping
✅ Found shared_preferences → SharedPreferences

✨ 2 adapter(s) detected!
📝 Generated: lib/blackbox_adapters.dart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Getting Started in 3 Steps
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt; — Add the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter pub add flutter_blackbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt; — Generate your adapters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dart run flutter_blackbox:init &lt;span class="nt"&gt;--generate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt; — Wrap your app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/foundation.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter_blackbox/flutter_blackbox.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'blackbox_adapters.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ← generated by CLI&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;dio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Dio&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;BlackBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;httpAdapters:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DioBlackBoxAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dio&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="nl"&gt;storageAdapters:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SharedPrefsStorageAdapter&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
    &lt;span class="nl"&gt;logAdapter:&lt;/span&gt; &lt;span class="n"&gt;PrintLogAdapter&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nl"&gt;trigger:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BlackBoxTrigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;floatingButton&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nl"&gt;enabled:&lt;/span&gt; &lt;span class="n"&gt;kDebugMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BlackBoxOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. BlackBox observes your existing Dio instance via interceptors — your API code is completely untouched.&lt;/p&gt;

&lt;p&gt;If you don't use Dio or any adapters, the Quick Start is even simpler. Just two lines and you get logs, crashes, FPS, widget rebuilds, device info, and QA reports immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;BlackBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;trigger:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BlackBoxTrigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;floatingButton&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nl"&gt;enabled:&lt;/span&gt; &lt;span class="n"&gt;kDebugMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BlackBoxOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Part That Saved My Team Hours
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;QA Reports&lt;/strong&gt; panel changed how we handle bug reports internally.&lt;/p&gt;

&lt;p&gt;Instead of the painful back-and-forth I described at the start, QA testers now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reproduce the bug&lt;/li&gt;
&lt;li&gt;Open BlackBox → QA tab&lt;/li&gt;
&lt;li&gt;Type the bug name&lt;/li&gt;
&lt;li&gt;Tap "Generate Report"&lt;/li&gt;
&lt;li&gt;Tap "Copy"&lt;/li&gt;
&lt;li&gt;Paste into the GitHub issue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Markdown report includes the full device spec, OS version, network connectivity, every API call made in the session (with request/response bodies), the user's navigation journey (route changes + taps), recent logs, and any crash stack traces. All automatically collected.&lt;/p&gt;

&lt;p&gt;Here's what the output looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Bug Report: User list not loading&lt;/span&gt;

&lt;span class="gs"&gt;**Device**&lt;/span&gt;: Pixel 6 Pro · Android 14 · API 34
&lt;span class="gs"&gt;**App**&lt;/span&gt;: v2.4.1 (build 47) · Debug
&lt;span class="gs"&gt;**Connectivity**&lt;/span&gt;: WiFi

&lt;span class="gu"&gt;## User Journey&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; App launched
&lt;span class="p"&gt;2.&lt;/span&gt; Navigated to /home
&lt;span class="p"&gt;3.&lt;/span&gt; Tapped "Users" tab
&lt;span class="p"&gt;4.&lt;/span&gt; Navigated to /users

&lt;span class="gu"&gt;## Failed Network Requests&lt;/span&gt;
GET /api/v1/users → 401 Unauthorized (234ms)
  Authorization: Bearer [REDACTED]
  Response: {"error": "Token expired"}

&lt;span class="gu"&gt;## Recent Logs&lt;/span&gt;
[ERROR] Auth: Token validation failed — tag: AuthService
[INFO]  Navigation: Pushed /users — tag: Router

&lt;span class="gu"&gt;## Crash Log&lt;/span&gt;
None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One tap. No Slack back-and-forth. No "can you send me the Proxyman export."&lt;/p&gt;




&lt;h2&gt;
  
  
  Observe Only, Never Modify
&lt;/h2&gt;

&lt;p&gt;This was a deliberate design principle and one I'm proud of.&lt;/p&gt;

&lt;p&gt;BlackBox hooks into your libraries through their built-in extension points. For Dio, it registers an &lt;code&gt;Interceptor&lt;/code&gt;. For &lt;code&gt;http&lt;/code&gt;, it wraps your existing &lt;code&gt;Client&lt;/code&gt;. For Socket.IO, it uses &lt;code&gt;socket.onAny()&lt;/code&gt;. For SharedPreferences, it reads via the existing API.&lt;/p&gt;

&lt;p&gt;Your code doesn't change. BlackBox is a silent observer. If you remove the package tomorrow, nothing in your codebase breaks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Widget Rebuild Tracker
&lt;/h2&gt;

&lt;p&gt;This one is genuinely useful for performance work, not just debugging.&lt;/p&gt;

&lt;p&gt;Toggle "AUTO ON" in the Rebuilds panel and BlackBox hooks into Flutter's &lt;code&gt;debugPrintRebuildDirtyWidgets&lt;/code&gt; flag. Every widget that rebuilds gets counted. The panel shows a heat-colored ranking — the most frequently rebuilding widget is at the top, color-coded red.&lt;/p&gt;

&lt;p&gt;You can spot a &lt;code&gt;setState()&lt;/code&gt; in the wrong place in about 10 seconds. No profiler session needed.&lt;/p&gt;

&lt;p&gt;For more surgical tracking, wrap specific widgets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;RebuildTracker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="s"&gt;'ProductCard'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;ProductCard&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;RebuildTracker&lt;/code&gt; is eliminated by Dart's tree-shaker in release builds via &lt;code&gt;kDebugMode&lt;/code&gt;. Zero production footprint.&lt;/p&gt;




&lt;h2&gt;
  
  
  API Mocking Without a Mock Server
&lt;/h2&gt;

&lt;p&gt;The mocking panel lets you intercept any API call and return a local response, without touching your API client code or spinning up a mock server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;BlackBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;pattern:&lt;/span&gt; &lt;span class="s"&gt;'/api/v1/user/profile'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;method:&lt;/span&gt; &lt;span class="s"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;response:&lt;/span&gt; &lt;span class="n"&gt;MockResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;statusCode:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'name'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Alice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'role'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Admin'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also set a network throttle via the slider in the Mocking panel — simulate 3G, high latency, or 0 kbps to test your loading states and timeout handling without leaving your desk.&lt;/p&gt;




&lt;h2&gt;
  
  
  Crashlytics / Sentry Integration
&lt;/h2&gt;

&lt;p&gt;If your team uses Firebase Crashlytics or Sentry, BlackBox can forward its captured telemetry to them automatically. Implement &lt;code&gt;BlackBoxObserver&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CrashlyticsObserver&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlackBoxObserver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;onCrash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CrashEntry&lt;/span&gt; &lt;span class="n"&gt;crash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;FirebaseCrashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;recordError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;crash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;crash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stackTrace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;onNetworkError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NetworkRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Forward failed requests to your monitoring tool&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;BlackBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;observers:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CrashlyticsObserver&lt;/span&gt;&lt;span class="p"&gt;()]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the CLI detects &lt;code&gt;firebase_crashlytics&lt;/code&gt; in your &lt;code&gt;pubspec.yaml&lt;/code&gt;, it auto-generates this observer for you. Every crash BlackBox captures gets forwarded to your Firebase dashboard, tagged for easy filtering.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sensitive Data is Redacted by Default
&lt;/h2&gt;

&lt;p&gt;The Storage Inspector auto-redacts values whose keys match patterns like &lt;code&gt;password&lt;/code&gt;, &lt;code&gt;token&lt;/code&gt;, &lt;code&gt;secret&lt;/code&gt;, &lt;code&gt;jwt&lt;/code&gt;, &lt;code&gt;pin&lt;/code&gt;, &lt;code&gt;auth&lt;/code&gt;, and 20+ more. They display as &lt;code&gt;••••••••&lt;/code&gt; and can't be copied or edited. You can customize the patterns per adapter or disable redaction entirely for internal dev-only builds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Storage Inspector for Any Backend
&lt;/h2&gt;

&lt;p&gt;The Storage Inspector isn't tied to SharedPreferences. Implement &lt;code&gt;BlackBoxStorageAdapter&lt;/code&gt; for any key-value store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetStorageAdapter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;BlackBoxStorageAdapter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;GetStorage&lt;/span&gt; &lt;span class="n"&gt;_box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;GetStorageAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;_box&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;'GetStorage'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;readAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_box&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;erase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple adapters each get their own tab inside the panel. SharedPreferences and GetStorage side by side, both inspectable and editable.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's in 0.6.x
&lt;/h2&gt;

&lt;p&gt;The latest release added several quality-of-life features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mini HUD Mode&lt;/strong&gt; — drag the floating button to the screen's bottom edge. It collapses into a persistent status bar showing live FPS, recent HTTP status codes, and crash count. Stays out of your way but always visible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swipe-to-Dismiss&lt;/strong&gt; — swipe left-to-right on any network request or log entry to remove it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HAR Export&lt;/strong&gt; — copy all network requests as HAR 1.2 JSON, importable directly into Postman or Chrome DevTools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolate JSON Parsing&lt;/strong&gt; — large response bodies are parsed off the main thread so the overlay never janks on heavy payloads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glassmorphic UI&lt;/strong&gt; — frosted-glass panels, gradient shines, blur effects, color-coded left accent borders on rows for instant status scanning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull-to-Refresh&lt;/strong&gt; — pull down on any list panel to refresh or reset data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Haptic Feedback&lt;/strong&gt; — tab switches and swipe actions provide tactile feedback.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Performance of the Overlay Itself
&lt;/h2&gt;

&lt;p&gt;BlackBox is built to be fast even when your app is under load.&lt;/p&gt;

&lt;p&gt;A few decisions made during development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lazy Panel Loading&lt;/strong&gt; — panels are created only when first navigated to, not at startup. Initial memory footprint is ~60% lower than eager loading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O(1) Network Lookups&lt;/strong&gt; — the network store uses &lt;code&gt;Map&lt;/code&gt;-based indexing, not linear scans.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FPS Callback via Engine Timestamp&lt;/strong&gt; — the frame callback uses the engine-provided timestamp parameter instead of &lt;code&gt;DateTime.now()&lt;/code&gt;, eliminating a syscall at 60fps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log Store Lazy Iteration&lt;/strong&gt; — filtering returns &lt;code&gt;Iterable&lt;/code&gt;, not &lt;code&gt;List&lt;/code&gt;, avoiding unnecessary allocations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debounced Search&lt;/strong&gt; — 300ms debounce on cross-panel search to prevent UI thread thrashing.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How to Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;flutter_blackbox&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^0.6.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter pub add flutter_blackbox
dart run flutter_blackbox:init &lt;span class="nt"&gt;--generate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📦 &lt;strong&gt;pub.dev&lt;/strong&gt;: &lt;a href="https://pub.dev/packages/flutter_blackbox" rel="noopener noreferrer"&gt;pub.dev/packages/flutter_blackbox&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐙 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/jaiminraval2704/flutter_blackbox" rel="noopener noreferrer"&gt;github.com/jaiminraval2704/flutter_blackbox&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;API Reference&lt;/strong&gt;: &lt;a href="https://pub.dev/documentation/flutter_blackbox/latest" rel="noopener noreferrer"&gt;pub.dev/documentation/flutter_blackbox/latest&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;The best debug tool is the one that's already in the build when something goes wrong. You don't always know in advance which API call will fail, which widget will rebuild 400 times, or what state your SharedPreferences will be in when QA files a ticket.&lt;/p&gt;

&lt;p&gt;flutter_blackbox is designed to always be there, collecting silently in the background, zero cost in production, ready when you need it.&lt;/p&gt;

&lt;p&gt;If you give it a try, I'd genuinely love to hear what you think — drop a comment, open a GitHub issue, or just hit the like button on pub.dev. Every piece of feedback has directly shaped what the package is today.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;flutter_blackbox is MIT licensed and available on pub.dev. Contributions welcome — see CONTRIBUTING.md.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tags: #flutter #dart #debugging #devtools #mobiledev #opensource&lt;/em&gt;&lt;/p&gt;

</description>
      <category>qa</category>
      <category>debug</category>
      <category>flutter</category>
      <category>dart</category>
    </item>
    <item>
      <title>I Got Tired of QA Sending Me "It Broke Again" Screenshots. So I Built This.</title>
      <dc:creator>Jaimin Raval</dc:creator>
      <pubDate>Tue, 19 May 2026 07:41:02 +0000</pubDate>
      <link>https://dev.to/jaimin_raval_f70ffdbbf594/i-got-tired-of-qa-sending-me-it-broke-again-screenshots-so-i-built-this-2i28</link>
      <guid>https://dev.to/jaimin_raval_f70ffdbbf594/i-got-tired-of-qa-sending-me-it-broke-again-screenshots-so-i-built-this-2i28</guid>
      <description>&lt;p&gt;You know that moment when your QA lead sends you a WhatsApp message at 11 PM that just says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"API not working again 🙂"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No logs. No steps to reproduce. No device info. Just vibes and a slightly passive-aggressive emoji.&lt;br&gt;
I've been that developer. Many times. And every time, I'd open Android Studio, ask them to reproduce it while I watched the console, squint at a 400-line log dump, and eventually find one line that mattered.&lt;br&gt;
This has to stop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem with Flutter debugging&lt;/strong&gt;&lt;br&gt;
Flutter is incredible for building UIs. But the debug experience — specifically for non-developer stakeholders like QA engineers and product managers — is genuinely broken.&lt;br&gt;
Here's what typically happens on a Flutter project:&lt;/p&gt;

&lt;p&gt;QA finds a bug&lt;br&gt;
QA sends a screenshot (sometimes)&lt;br&gt;
Developer asks: "What was the API response?"&lt;br&gt;
QA doesn't know. They just saw a spinner and then nothing.&lt;br&gt;
Developer asks: "Can you reproduce it?"&lt;br&gt;
QA tries. It doesn't reproduce. The bug disappears into the void.&lt;/p&gt;

&lt;p&gt;Meanwhile, on the web, you have browser DevTools. On React Native, you have Flipper. On iOS, you have the Instruments suite.&lt;br&gt;
Flutter has... print().&lt;/p&gt;

&lt;p&gt;What I actually wanted&lt;br&gt;
I wanted one thing: a debug overlay that QA can use themselves, without needing a connected Mac or developer standing next to them.&lt;br&gt;
Specifically:&lt;/p&gt;

&lt;p&gt;See every API request and response in real time&lt;br&gt;
Inspect device info, app version, connectivity&lt;br&gt;
See what the app was doing before the bug happened&lt;br&gt;
Generate a report they can paste directly into Jira&lt;/p&gt;

&lt;p&gt;So I built it. It's called flutter_blackbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it does&lt;/strong&gt;&lt;br&gt;
flutter_blackbox is an in-app debug overlay for Flutter. Shake your device (or tap a floating button) and a full debug panel slides up. No computer attached. No build flags. No special setup.&lt;br&gt;
Here's what's inside:&lt;/p&gt;

&lt;p&gt;🌐 &lt;strong&gt;Network Inspector&lt;/strong&gt;&lt;br&gt;
Every HTTP request your app makes — headers, request body, response body, status code, timing — captured in real time. Works with Dio and the http package.&lt;br&gt;
No more "what did the API return?" conversations.&lt;/p&gt;

&lt;p&gt;📋 &lt;strong&gt;Live Logs&lt;/strong&gt;&lt;br&gt;
Auto-captures every print() and debugPrint() call. Also supports manual logging with levels (info, warning, error) and tags. All filterable.&lt;/p&gt;

&lt;p&gt;⚡ &lt;strong&gt;Performance Monitor&lt;/strong&gt;&lt;br&gt;
Live FPS graph. Jank detection. Memory alerts. See exactly when your app starts struggling.&lt;/p&gt;

&lt;p&gt;🔄 &lt;strong&gt;Widget Rebuild Tracker&lt;/strong&gt;&lt;br&gt;
Toggle AUTO mode and it tracks every widget rebuild automatically. Sorted by rebuild count, heat-colored. Find your worst offenders in 10 seconds.&lt;/p&gt;

&lt;p&gt;💾 &lt;strong&gt;Storage Inspector&lt;/strong&gt;&lt;br&gt;
Browse, search, edit, and delete SharedPreferences, GetStorage, or Hive keys — directly from the overlay. Sensitive keys like token, password, jwt are auto-redacted.&lt;/p&gt;

&lt;p&gt;🔌 &lt;strong&gt;Socket.IO Inspector&lt;/strong&gt;&lt;br&gt;
Auto-captures all incoming Socket.IO events. Zero code changes to your socket layer.&lt;/p&gt;

&lt;p&gt;📝 &lt;strong&gt;One-Tap QA Reports&lt;/strong&gt;&lt;br&gt;
This is the feature that changed my team's workflow.&lt;br&gt;
QA taps the QA tab, types a bug name, hits Generate Report. BlackBox compiles a Markdown report with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Screenshot&lt;/strong&gt;&lt;br&gt;
Device info (model, OS, DPI, connectivity)&lt;br&gt;
User journey (every route change and tap leading up to the bug)&lt;br&gt;
All failed network requests with full payloads&lt;br&gt;
Recent logs&lt;br&gt;
Any crash stack traces&lt;/p&gt;

&lt;p&gt;One tap Copy → paste into GitHub Issues, Jira, Linear, or Slack.&lt;br&gt;
My QA lead went from "it broke again 🙂" to sending me full reproduction reports with API responses included. This is real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero runtime cost — for real&lt;/strong&gt;&lt;br&gt;
Everything compiles out in release builds via Dart's tree-shaker and kDebugMode. BlackBox doesn't exist in your production APK.&lt;br&gt;
The package itself is 63 KB with no optional dependencies bundled.&lt;/p&gt;

&lt;p&gt;The part I'm most proud of: the CLI&lt;br&gt;
Here's the thing about debug overlay packages — they usually require you to install their HTTP client, their logger, their storage solution. Which means your app gets heavier and you're locked into their choices.&lt;br&gt;
BlackBox does it differently.&lt;br&gt;
It ships with a built-in CLI that reads your pubspec.yaml, detects what libraries you already use, and generates the adapter code for your project:&lt;br&gt;
&lt;code&gt;bashdart run flutter_blackbox:init --generate&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;`Output:&lt;br&gt;
🐞 BlackBox Init — Auto-detecting your dependencies...&lt;/p&gt;

&lt;p&gt;✅ Found dio → Dio HTTP client&lt;br&gt;
⬚  http → not found, skipping&lt;br&gt;
✅ Found shared_preferences → SharedPreferences&lt;/p&gt;

&lt;p&gt;✨ 2 adapter(s) detected!&lt;br&gt;
📝 Generated: lib/blackbox_adapters.dart&lt;br&gt;
You get a generated blackbox_adapters.dart tailored to your project. If you don't use http, you don't get the http adapter. Zero bloat.&lt;br&gt;
This means BlackBox observes your existing code through built-in extension points (Dio interceptors, http client wrappers, SharedPreferences hooks) — it never modifies your trusted code.`&lt;/p&gt;

&lt;p&gt;Integration is 3 steps&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash# Step 1
flutter pub add flutter_blackbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Step 2&lt;/span&gt;
dart run flutter_blackbox:init &lt;span class="nt"&gt;--generate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="ss"&gt;#&lt;/span&gt; &lt;span class="n"&gt;Step&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Update&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;dart&lt;/span&gt;
&lt;span class="n"&gt;dartimport&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/foundation.dart'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter_blackbox/flutter_blackbox.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'blackbox_adapters.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ← generated by CLI&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;BlackBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;httpAdapters:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DioBlackBoxAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dio&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="nl"&gt;storageAdapters:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SharedPrefsStorageAdapter&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
    &lt;span class="nl"&gt;trigger:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BlackBoxTrigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shake&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// or .floatingButton()&lt;/span&gt;
    &lt;span class="nl"&gt;enabled:&lt;/span&gt; &lt;span class="n"&gt;kDebugMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BlackBoxOverlay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Your existing Dio calls, socket events, and SharedPreferences are now fully observable.&lt;/p&gt;

&lt;p&gt;What changed on my team&lt;br&gt;
Before BlackBox:&lt;/p&gt;

&lt;p&gt;QA files a vague bug report&lt;br&gt;
Developer asks for reproduction steps&lt;br&gt;
2–3 back-and-forth messages&lt;br&gt;
Developer eventually reproduces it locally&lt;br&gt;
Developer fixes it, probably&lt;/p&gt;

&lt;p&gt;After BlackBox:&lt;/p&gt;

&lt;p&gt;QA reproduces the bug&lt;br&gt;
QA generates report (one tap)&lt;br&gt;
Report lands in Jira with full API payload and device state&lt;br&gt;
Developer fixes it in half the time&lt;/p&gt;

&lt;p&gt;The back-and-forth is gone. The "it doesn't reproduce for me" problem is mostly gone. The passive-aggressive emojis are... still there. But at least they come with data now.&lt;/p&gt;

&lt;p&gt;What's new in v0.5.0 — Crashlytics &amp;amp; Sentry integration&lt;br&gt;
The biggest ask from the community after launch was: "Can BlackBox forward crashes to our existing monitoring tools?"&lt;br&gt;
v0.5.0 ships the BlackBoxObserver API. It lets professional teams pipe BlackBox telemetry directly into Crashlytics, Sentry, Datadog, or any custom Slack webhook — without breaking the zero-dependency rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;dartBlackBox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;observers:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;BlackBoxObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;onCrash:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;FirebaseCrashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;recordError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;crash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;crash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stackTrace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;reason:&lt;/span&gt; &lt;span class="n"&gt;crash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toFormattedString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// one-liner readable payload&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nl"&gt;onNetworkError:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Sentry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;captureMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'API failed: &lt;/span&gt;&lt;span class="si"&gt;${req.url}&lt;/span&gt;&lt;span class="s"&gt; → &lt;/span&gt;&lt;span class="si"&gt;${res?.statusCode}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// ...rest of setup&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use firebase_crashlytics, the CLI now detects it automatically and generates a CrashlyticsObserver implementation pre-tagged for easy filtering in the Firebase dashboard:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bashdart run flutter_blackbox:init --generate&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  ✅ Found firebase_crashlytics → CrashlyticsObserver
&lt;/h1&gt;

&lt;h1&gt;
  
  
  📝 Generated: lib/blackbox_adapters.dart
&lt;/h1&gt;

&lt;p&gt;There's also a small but important UX fix: the Storage Inspector now shows a confirmation dialog before "Clear All" — because accidentally wiping SharedPreferences on a client's device during a demo is not a great look.&lt;/p&gt;

&lt;p&gt;What's next&lt;br&gt;
The package is actively maintained. On the roadmap:&lt;/p&gt;

&lt;p&gt;GraphQL request inspector&lt;br&gt;
Network throttling presets (simulate 3G, offline)&lt;br&gt;
VS Code extension for report visualization&lt;/p&gt;

&lt;p&gt;Try it&lt;br&gt;
yamldependencies:&lt;br&gt;
  &lt;code&gt;flutter_blackbox: ^0.5.0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//pub.dev/packages/flutter_blackbox"&gt;pub.dev&lt;/a&gt;&lt;br&gt;
&lt;a href="//github.com/jaimincapermint/flutter_blackbox"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If it saves you one "it broke again 🙂" conversation, leave a like on pub.dev. It helps other Flutter devs find it.&lt;/p&gt;

&lt;p&gt;Built by a Flutter developer who got tired of standing next to QA with a MacBook. Questions and feedback welcome in the comments.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>dart</category>
      <category>flutter</category>
    </item>
  </channel>
</rss>
