<?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 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>
