api_track_inspector 2.0.0 copy "api_track_inspector: ^2.0.0" to clipboard
api_track_inspector: ^2.0.0 copied to clipboard

Dependency-free Flutter network inspector for debug builds. Logs any HTTP client (Dio, http, chopper) and shows requests in an in-app viewer behind a draggable FAB.

api_track_inspector #

Inspect every API call in minutes, not hours. api_track_inspector adds a draggable in-app monitor with request details, timing, errors and export — and depends on nothing but the Flutter SDK.

dependencies:
  api_track_inspector: ^2.0.0

pub package License: MIT

Zero dependencies, on purpose #

A debugging tool should never dictate your architecture. As of 2.0.0 this package has an empty dependencies: block — no GetX, no state-management library, no sizing library, no sharing plugin.

That means it:

  • works the same under Bloc, Riverpod, Provider, GetX, signals or plain setState;
  • needs no navigatorKey, no service locator, no ScreenUtilInit wrapper;
  • can never drag your transitive dependency versions around.

Upgrading from 1.x? See Migrating from 1.x — it is a small, mechanical change.

Screenshots #

The carousel at the top of this package on pub.dev uses the same five images (from pubspec.yaml screenshots).

Preview (order: list → POST detail → bodies → error → 403 response):

Request list POST request detail Request and response bodies

Error detail 403 response and headers

Setup #

1. Initialize #

import 'package:flutter/foundation.dart';
import 'package:api_track_inspector/api_track_inspector.dart';

void main() {
  NetworkInspector.init(enabled: kDebugMode);
  runApp(const MyApp());
}

Keep it off in production. Logs hold full request and response bodies, including credentials, and the export button can copy them out of the app. Gate on kDebugMode, a dev flavor, or both.

2. Mount the FAB #

MaterialApp(
  builder: (context, child) => NetworkInspector.wrapWithFAB(child!),
);

Works identically with MaterialApp.router, GoRouter, or any other routing setup. When enabled is false, wrapWithFAB returns child untouched, so it is safe to leave in release builds.

3. Log your traffic #

Two calls: open a log on request, close it on response.

Dio

Copy this interceptor into your project (the package does not ship it — that would mean depending on Dio):

class InspectorInterceptor extends Interceptor {
  static const _logIdKey = '_inspectorLogId';
  static const _startKey = '_inspectorStart';

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    final logId = NetworkInspector.logRequest(
      method: options.method,
      url: options.uri.toString(),
      headers: Map<String, dynamic>.from(options.headers),
      body: options.data,
    );
    if (logId != null) {
      options.extra[_logIdKey] = logId;
      options.extra[_startKey] = DateTime.now();
    }
    handler.next(options);
  }

  @override
  void onResponse(Response<dynamic> res, ResponseInterceptorHandler handler) {
    _complete(res.requestOptions, statusCode: res.statusCode, body: res.data);
    handler.next(res);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    _complete(
      err.requestOptions,
      statusCode: err.response?.statusCode,
      body: err.response?.data,
      error: err.message ?? err.type.name,
    );
    handler.next(err);
  }

  void _complete(
    RequestOptions options, {
    int? statusCode,
    dynamic body,
    String? error,
  }) {
    final logId = options.extra[_logIdKey] as String?;
    final start = options.extra[_startKey] as DateTime?;
    if (logId == null) return;
    NetworkInspector.logResponse(
      logId: logId,
      statusCode: statusCode,
      body: body,
      duration: start == null ? null : DateTime.now().difference(start),
      error: error,
    );
  }
}

Register it last in your interceptor chain, so it observes headers your auth interceptor has already attached.

package:http (or anything else)

final logId = NetworkInspector.logRequest(method: 'GET', url: uri.toString());
final startedAt = DateTime.now();

final response = await http.get(uri);

if (logId != null) {
  NetworkInspector.logResponse(
    logId: logId,
    statusCode: response.statusCode,
    body: response.body,
    duration: DateTime.now().difference(startedAt),
  );
}

Opening the inspector #

  • The FAB — draggable, snaps to the nearest edge, tap to open. Tap the barrier, the close button, or the FAB again to dismiss.
  • From your own codeNetworkInspector.show(context) pushes it as a normal dialog route, so the system back button closes it.
IconButton(
  icon: const Icon(Icons.bug_report),
  onPressed: () => NetworkInspector.show(context),
);

context must be under a Navigator — any widget inside your screens qualifies. A context taken from MaterialApp.builder does not, because that builder runs above the Navigator; that is exactly why the FAB presents the inspector itself instead of pushing a route.

Sharing and export #

By default the export and share buttons copy JSON to the clipboard, so the package needs no sharing plugin. To use a native share sheet, pass a handler:

NetworkInspector.init(
  enabled: kDebugMode,
  onShare: (data, subject) => SharePlus.instance.share(
    ShareParams(text: data, subject: subject),
  ),
);

Configuration #

Option Default Purpose
enabled true Master switch. False makes every API a no-op.
maxLogs 100 Ring-buffer size; oldest entries are dropped.
primaryColor / secondaryColor Blue FAB and header gradient.
fabBottomPosition / fabRightPosition 100 / 16 Starting FAB position.
showShareButton true Per-log share button.
onShare null Share handler; falls back to the clipboard.

API #

Member Purpose
NetworkInspector.init(...) Configure and start.
NetworkInspector.wrapWithFAB(child) Mount the draggable FAB.
NetworkInspector.show(context) Open as a dialog route.
NetworkInspector.logRequest(...) Open a log; returns its id, or null when disabled.
NetworkInspector.logResponse(...) Close a log by id.
NetworkInspector.clearLogs() Drop all logs.
NetworkInspector.exportLogs() All logs as a JSON string.
NetworkInspector.service The NetworkInspectorService (a ChangeNotifier).
NetworkInspector.maybeService Same, but null instead of throwing.
NetworkInspector.reset() Tear down and drop logs (useful in tests).

NetworkInspectorService is a plain ChangeNotifier, so you can build your own UI on it:

ListenableBuilder(
  listenable: NetworkInspector.service,
  builder: (context, _) => Text('${NetworkInspector.service.logs.length}'),
);

Migrating from 1.x #

2.0.0 removes GetX. Everything below is mechanical:

1.x 2.0.0
NetworkInspector.showDialog() NetworkInspector.show(context)
NetworkInspector.onRequest(request) (GetConnect) Removed — use logRequest / logResponse
NetworkInspector.onResponse(req, res) (GetConnect) Removed — as above
NetworkInspector.setRequestBody(body) Removed — pass body: to logRequest
Share used share_plus automatically Pass onShare:, or accept the clipboard fallback
Host app needed ScreenUtilInit No longer required
NetworkInspectorService extends GetxService extends ChangeNotifier

GetConnect users: the two convenience wrappers are gone because they took GetX types in their signature. Call logRequest / logResponse from your own request and response modifiers instead — same two calls as every other client.

If you still want the FAB and the GetX-based dialog, pin api_track_inspector: 1.1.18.

Requirements #

  • Flutter >=3.27.0, Dart >=3.6.0.

License #

MIT

1
likes
160
points
137
downloads
screenshot

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Dependency-free Flutter network inspector for debug builds. Logs any HTTP client (Dio, http, chopper) and shows requests in an in-app viewer behind a draggable FAB.

Repository (GitHub)
View/report issues

Topics

#networking #debugging #http #dio #devtools

License

MIT (license)

Dependencies

flutter

More

Packages that depend on api_track_inspector