flutter_http_watcher 1.2.2
flutter_http_watcher: ^1.2.2 copied to clipboard
A lightweight in-app network inspector for Flutter. Floating draggable button with full request/response viewer. Works with any HTTP client.
flutter_http_watcher #
A lightweight in-app network inspector for Flutter.
Works with any HTTP client — http, dio, retrofit, graphql, or your own. Zero HTTP dependencies.
Screenshots #
Features #
- Draggable floating button with live request count
- Live connectivity dot — green (online) · red (offline) · grey (unknown)
- Error badge — red badge on the floating button shows 4xx / 5xx / failed count
- Custom icon — replace the default button icon via
HttpWatcherOverlay(icon: ...) - Color-coded by HTTP method (GET / POST / PUT / DELETE)
- Color-coded status codes (green 2xx · orange 4xx · red 5xx)
- Search bar + method & status code filter chips
- Full request & response viewer with JSON pretty-printing
- cURL export — copy any request as a
curlcommand - Request replay — re-send any logged request with one tap
- Stats screen — success rate, avg duration, top hosts, slowest requests
- Export logs — save as
.txtor export as.har(Postman / Charles / DevTools compatible) - Web Viewer — open live logs in any browser on the same WiFi network
- Dark / light theme toggle
- One-tap copy · share full request as text
- Pause / resume logging
- Works with any HTTP client — zero HTTP dependencies
- Controlled entirely by the
showflag — use in debug, release, or staging
Installation #
dependencies:
flutter_http_watcher: ^1.2.2
Setup #
1 — Wrap your app #
Add HttpWatcherOverlay as the outermost widget in your MaterialApp builder. It requires your app's navigatorKey so it can open the inspector screen above your navigation stack.
import 'package:flutter_http_watcher/network_inspector.dart';
final navigatorKey = GlobalKey<NavigatorState>();
MaterialApp(
navigatorKey: navigatorKey,
builder: (context, child) {
return HttpWatcherOverlay(
navigatorKey: navigatorKey,
show: true, // set to false to hide the button entirely
child: child!,
);
},
);
Using GetX? Pass
Get.keyas thenavigatorKey.
Using go_router? Pass yourGlobalKey<NavigatorState>the same way.
2 — Log requests #
The package has no built-in HTTP client. Copy one of the adapters below into your project and use it instead of your normal client.
http package
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter_http_watcher/network_inspector.dart';
class WatcherHttpClient extends http.BaseClient {
final http.Client _inner;
WatcherHttpClient([http.Client? inner]) : _inner = inner ?? http.Client();
@override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
final start = DateTime.now();
final streamed = await _inner.send(request);
final bytes = await streamed.stream.toBytes();
HttpWatcherLogger.instance.logRequest(
method: request.method,
url: request.url.toString(),
headers: Map<String, String>.from(request.headers),
body: request is http.Request ? request.body : null,
statusCode: streamed.statusCode,
responseBody: utf8.decode(bytes, allowMalformed: true),
startTime: start,
);
return http.StreamedResponse(Stream.value(bytes), streamed.statusCode,
headers: streamed.headers, contentLength: bytes.length);
}
@override
void close() => _inner.close();
}
Use it like this:
final client = WatcherHttpClient();
final response = await client.get(Uri.parse('https://api.example.com/users'));
dio
Add this interceptor to your Dio instance:
import 'package:dio/dio.dart';
import 'package:flutter_http_watcher/network_inspector.dart';
class WatcherDioInterceptor extends Interceptor {
final _starts = <int, DateTime>{};
@override
void onRequest(RequestOptions o, RequestInterceptorHandler h) {
_starts[o.hashCode] = DateTime.now();
h.next(o);
}
@override
void onResponse(Response r, ResponseInterceptorHandler h) {
final start = _starts.remove(r.requestOptions.hashCode) ?? DateTime.now();
HttpWatcherLogger.instance.logRequest(
method: r.requestOptions.method,
url: r.requestOptions.uri.toString(),
headers: r.requestOptions.headers.map((k, v) => MapEntry(k, v.toString())),
body: r.requestOptions.data,
statusCode: r.statusCode ?? 0,
responseBody: r.data?.toString() ?? '',
startTime: start,
);
h.next(r);
}
@override
void onError(DioException e, ErrorInterceptorHandler h) {
final start = _starts.remove(e.requestOptions.hashCode) ?? DateTime.now();
HttpWatcherLogger.instance.logRequest(
method: e.requestOptions.method,
url: e.requestOptions.uri.toString(),
headers: e.requestOptions.headers.map((k, v) => MapEntry(k, v.toString())),
body: e.requestOptions.data,
statusCode: e.response?.statusCode ?? 0,
responseBody: e.response?.data?.toString() ?? e.message ?? '',
startTime: start,
);
h.next(e);
}
}
Add it to your Dio instance:
final dio = Dio();
dio.interceptors.add(WatcherDioInterceptor());
Any other client (manual)
Call logRequest manually after every response:
final start = DateTime.now();
final response = await myClient.get(uri);
HttpWatcherLogger.instance.logRequest(
method: 'GET',
url: uri.toString(),
statusCode: response.statusCode,
responseBody: response.body,
startTime: start,
);
Inspector screen #
Tap the floating button to open the inspector. All options are in the ⋮ menu (top-right corner):
| Option | Description |
|---|---|
| Stats | Success rate, average duration, by-method breakdown, top hosts, slowest requests |
| Save as .txt | Export all logs as a plain text file and share it |
| Export as .har | Export in HAR format — importable in Postman, Charles Proxy, or browser DevTools |
| Dark / Light mode | Toggle the inspector theme |
| Pause / Resume | Stop or start capturing new requests |
| Clear all | Delete all logged requests |
Request list #
- Requests are shown newest-first with method, URL path, status code, and duration
- Use the search bar to filter by URL, method, or status code
- Use the method chips (GET / POST / PUT / DELETE) to filter by HTTP method
- Use the status chips (2xx / 4xx / 5xx / Error) to filter by response type
Request detail #
Tap any request row to open the detail screen. From here you can:
| Action | How |
|---|---|
| Copy as cURL | Tap the cURL icon in the app bar — ready to paste in any terminal |
| Replay request | Tap the replay icon — re-sends the exact same request and logs the new response |
| Share | Tap the share icon to share the full request + response as text |
| Copy section | Tap the copy icon next to any section (headers, body, response) |
Floating button #
The floating button is draggable — press and drag it to any edge of the screen.
| Element | Meaning |
|---|---|
| 🟢 / 🔴 / ⚪ dot | Live connectivity status (online / offline / unknown) |
| Number | Total logged request count |
| Red badge | Number of 4xx / 5xx / failed requests since last clear |
| ▶ icon | Logging is paused — tap to resume |
Connectivity indicator #
The dot on the floating button reflects live network connectivity, checked every 5 seconds.
| Color | Meaning |
|---|---|
| 🟢 Green | Device has an active internet connection |
| 🔴 Red | Device is offline |
| ⚪ Grey | Status not yet determined (app just started) |
Stats screen #
Open via ⋮ → Stats. Shows:
- Total requests, success count, client error count, server error count
- Success rate progress bar
- Average response duration
- By-method breakdown (GET / POST / PUT / DELETE)
- Top 5 hosts by request count
- Top 5 slowest requests
Web Viewer #
Start a local HTTP server on the device and open the logs in any browser on the same WiFi network — useful for viewing on a laptop while the app runs on a phone.
- Tap ⋮ → Web Viewer
- The server starts and a URL appears (e.g.
http://192.168.1.5:9742) - Open that URL in any browser on the same WiFi
- The page updates every 3 seconds with new requests
The browser page includes:
- Search bar — filter by URL, method, or status
- Method chips (GET / POST / PUT / DELETE)
- Status chips (2xx / 4xx / 5xx / Error)
- Click any row to see full request details
- Copy buttons on every section (URL, cURL, headers, request body, response body)
// Control programmatically:
await HttpWatcherLogger.instance.startWebServer();
print(HttpWatcherLogger.instance.webServerUrl); // http://192.168.x.x:9742
await HttpWatcherLogger.instance.stopWebServer();
Note: The web viewer runs on port
9742. Make sure your device firewall allows connections on that port.
Configuration #
// Disable logging at runtime (overlay stays visible, no new logs captured):
HttpWatcherLogger.instance.enabled = false;
// Toggle logging on/off:
HttpWatcherLogger.instance.toggleEnabled();
// Change the maximum number of entries kept in memory (default: 300):
HttpWatcherLogger.instance.maxEntries = 100;
// Read the current error count (4xx / 5xx / failed requests):
final errors = HttpWatcherLogger.instance.errorCount;
Custom icon #
Replace the default network_check icon with any Flutter IconData:
HttpWatcherOverlay(
navigatorKey: navigatorKey,
icon: Icons.bug_report_outlined, // any IconData
child: child!,
)
Hide in production #
Control visibility with the show flag — no need to remove any code:
HttpWatcherOverlay(
navigatorKey: navigatorKey,
show: kDebugMode, // true in debug, false in release
child: child!,
)
License #
MIT