flutter_http_watcher 1.2.2 copy "flutter_http_watcher: ^1.2.2" to clipboard
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 curl command
  • Request replay — re-send any logged request with one tap
  • Stats screen — success rate, avg duration, top hosts, slowest requests
  • Export logs — save as .txt or 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 show flag — 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.key as the navigatorKey.
Using go_router? Pass your GlobalKey<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.

  1. Tap ⋮ → Web Viewer
  2. The server starts and a URL appears (e.g. http://192.168.1.5:9742)
  3. Open that URL in any browser on the same WiFi
  4. 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

1
likes
140
points
468
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A lightweight in-app network inspector for Flutter. Floating draggable button with full request/response viewer. Works with any HTTP client.

Repository (GitHub)
View/report issues

Topics

#network #http #debugging #inspector #developer-tools

License

MIT (license)

Dependencies

flutter, path_provider, share_plus, url_launcher

More

Packages that depend on flutter_http_watcher