flutter_dev_toolkit 1.4.0 copy "flutter_dev_toolkit: ^1.4.0" to clipboard
flutter_dev_toolkit: ^1.4.0 copied to clipboard

A modular in-app developer console for Flutter: logs, network inspector with mocking, crash reports, performance, storage, feature flags, and a plugin system.

Flutter Dev Toolkit #

πŸš€ A modular in-app developer console for Flutter apps.

Track logs, API calls, navigation, lifecycle events, screen transitions, app state, and more β€” all in real time, inside your app.

pub package GitHub


✨ Features #

  • βœ… In-app Dev Console with a draggable floating overlay
  • βœ… Colored logs with filtering and tagging
  • βœ… Network call inspector (supports http, dio, and retrofit)
  • βœ… Route stack and screen duration tracker
  • βœ… Search and filtering β€” by level and tag in logs, by method and status class in network
  • βœ… Crash reporter β€” Flutter and unhandled async errors with stack traces
  • βœ… Performance monitor β€” FPS, memory (RSS), startup time, jank frames, and a rolling history sparkline
  • βœ… Deep link inspector with query parameter breakdown
  • βœ… Storage inspector β€” view, add, edit and delete SharedPreferences entries live
  • βœ… Runtime feature flags β€” flip app-registered flags without a rebuild
  • βœ… Network response mocking β€” short-circuit a request with a canned status/body/delay
  • βœ… Lifecycle event logging
  • βœ… Device info panel
  • βœ… Export logs, network calls (JSON, cURL, HAR 1.2) and route data
  • βœ… Plugin system for adding custom tools
  • βœ… App State Inspector (Bloc built in, Riverpod/Provider in a few lines)
  • βœ… Light and dark console themes
  • βœ… Suppressed in release builds by default

πŸ›  Installation #

Add to your pubspec.yaml:

dependencies:
  flutter_dev_toolkit: ^latest_version

Then:

flutter pub get

πŸš€ Getting Started #

1. Initialize the toolkit #

void main() {
  FlutterDevToolkit.init(
    config: DevToolkitConfig(
      logger: DefaultLogger(),

      // All built-in plugins are enabled by default. List the ones you want
      // to leave out:
      disableBuiltInPlugins: [
        // BuiltInPluginType.logs,
        // BuiltInPluginType.network,
        // BuiltInPluginType.routes,
        // BuiltInPluginType.deviceInfo,
        // BuiltInPluginType.crashes,
        // BuiltInPluginType.performance,
        // BuiltInPluginType.deepLinks,
        // BuiltInPluginType.storage,
        // BuiltInPluginType.featureFlags,
      ],
    ),
  );

  runApp(MyApp());
}

init() installs FlutterError.onError and PlatformDispatcher.onError handlers so the Crashes tab can record errors. If you install your own handler afterwards, chain to the previous one β€” replacing it outright silently stops crash capture:

final previousOnError = FlutterError.onError;
FlutterError.onError = (details) {
  myCrashReporter.record(details);
  previousOnError?.call(details);
};

Configuration options #

Option Default Purpose
logger β€” The LoggerInterface backing the Logs tab
disableBuiltInPlugins [] Built-in plugins to leave out
enableInRelease false Whether the overlay is active in release builds
maxLogEntries 2000 Log entries retained in memory
maxNetworkLogs 500 Network calls retained in memory
logFrameDrops false Also write every dropped frame to the Logs tab
theme DevConsoleTheme.dark Console's starting theme; toggleable at runtime

Release builds #

The toolkit is suppressed in release builds unless you opt in with enableInRelease: true. When suppressed, DevOverlay renders nothing and FlutterDevToolkit.logger becomes a no-op, so your logging calls stay safe in production.

2. Add Dev Console Overlay #

MaterialApp(
  builder: (context, child) {
    return Stack(
      children: [
        child!,
        const DevOverlay(),
      ],
    );
  },
  navigatorObservers: [RouteInterceptor.instance],
);

πŸ”Œ Network Setup #

πŸ”Ή Using http package #

Replace the default client with HttpInterceptor from the toolkit:

import 'package:http/http.dart' as http;
import 'package:flutter_dev_toolkit/flutter_dev_toolkit.dart';

final client = HttpInterceptor(); // Instead of http.Client()

final response = await client.get(Uri.parse('https://example.com'));

πŸ”Ή Using dio #

Register Dio interceptor:

final dio = Dio();
dio.interceptors.add(DioNetworkInterceptor());

πŸ”Ή Using retrofit #

Pass the configured Dio instance to your Retrofit client:

final api = MyApiClient(Dio()..interceptors.add(DioNetworkInterceptor()));

Mocking a response #

Manage mock rules from the Network tab's config panel (the tune icon in its app bar), or add one directly:

NetworkMockStore.add(NetworkMockRule(
  urlContains: '/api/users',   // case-insensitive substring match
  method: 'GET',               // null matches any method
  statusCode: 200,
  responseBody: '{"id": 1, "name": "Ada"}',
  delay: Duration(milliseconds: 300), // simulate latency; defaults to none
));

A matching request never reaches the network β€” both HttpInterceptor and DioNetworkInterceptor short-circuit it β€” and shows up in the Network tab tagged MOCKED. The first enabled rule that matches wins.


🧩 Plugins #

You can add custom developer tools as plugins:

class CounterPlugin extends DevToolkitPlugin {
  @override 
  String get name => 'Counter';

  @override 
  IconData get icon => Icons.exposure_plus_1;

  @override 
  void onInit() => debugPrint('CounterPlugin loaded!');

  @override 
  Widget buildTab(BuildContext context) => Center(child: Text('Counter Tab'));
}

FlutterDevToolkit.registerPlugin(CounterPlugin());

Deep link capture is routing-agnostic β€” call the observer wherever your app receives a link:

// uni_links
uriLinkStream.listen((uri) {
  if (uri != null) {
    DevToolkitDeepLinkObserver.onLinkReceived(uri.toString());
  }
});

// go_router
GoRouter(
  redirect: (context, state) {
    DevToolkitDeepLinkObserver.onLinkReceived(
      state.uri.toString(),
      source: 'go_router',
    );
    return null;
  },
);

πŸ—„οΈ Storage Inspector #

The Storage tab reads and writes the app's SharedPreferences directly β€” no setup needed beyond enabling the plugin (it's on by default). Add, edit, and delete entries of any type SharedPreferences supports (bool, int, double, String, List<String>), with search and JSON export.

Since SharedPreferences has no change notifications of its own, the tab loads a snapshot on open and after every edit made through it; it won't pick up a write your app makes directly while the tab happens to be open β€” use the refresh button for that.


🚩 Feature Flags #

Register a flag once, anywhere in your app, and it shows up in the Flags tab β€” flip it at runtime, no rebuild:

final showNewCheckout = FeatureFlagStore.register(FeatureFlag(
  key: 'new_checkout_flow',
  label: 'New Checkout Flow',
  type: FeatureFlagType.boolean,
  defaultValue: false,
));

// Anywhere that needs to react to it:
ValueListenableBuilder(
  valueListenable: showNewCheckout.notifier,
  builder: (_, enabled, __) =>
      (enabled as bool) ? const NewCheckout() : const OldCheckout(),
);

FeatureFlagType also supports string, number, and options (a fixed set of values, via FeatureFlag.options). Re-registering the same key β€” safe to do on every main() run, including hot reload β€” returns the existing flag rather than resetting whatever it's currently set to.


πŸ” App State Inspector #

Inspect state transitions, showing each change's previous and current value.

Bloc works out of the box, since the toolkit already depends on bloc:

Bloc.observer = DevBlocObserver();

FlutterDevToolkit.registerPlugin(
  AppStateInspectorPlugin([
    BlocAdapter(),
  ]),
);

Other state frameworks #

Rather than depend on every state library, the toolkit accepts changes pushed in from your app through RecordedStateAdapter. Each framework is a few lines.

Riverpod:

final riverpodInspector = RecordedStateAdapter(name: 'Riverpod');

class DevToolkitProviderObserver extends ProviderObserver {
  @override
  void didUpdateProvider(provider, previousValue, newValue, container) {
    riverpodInspector.record(
      provider.name ?? provider.runtimeType.toString(),
      newValue,
      previous: previousValue,
    );
  }
}

runApp(
  ProviderScope(
    observers: [DevToolkitProviderObserver()],
    child: MyApp(),
  ),
);

Provider / ChangeNotifier:

final providerInspector = RecordedStateAdapter(name: 'Provider');

class CartModel extends ChangeNotifier {
  CartModel() {
    addListener(() => providerInspector.record('CartModel', items));
  }
}

Then register whichever adapters you use:

FlutterDevToolkit.registerPlugin(
  AppStateInspectorPlugin([BlocAdapter(), riverpodInspector]),
);

πŸ“ Logging #

FlutterDevToolkit.logger.log('Message');
FlutterDevToolkit.logger.log('Error occurred', level: LogLevel.error);

πŸ“€ Exporting #

You can export relevant data directly from each plugin’s tab:

  • Logs Plugin β†’ Export filtered logs
  • Network Plugin β†’ Export captured network calls as JSON, as a runnable cURL command, or as a HAR 1.2 archive you can open in Chrome DevTools, Postman or Charles Proxy
  • Route Tracker β†’ Export route stack and navigation history
  • Crashes β†’ Export captured errors with stack traces
  • Deep Links β†’ Export recorded links as JSON
  • Storage β†’ Export all SharedPreferences entries as JSON
  • Flags β†’ Export all registered feature flags and their current values as JSON

πŸ“„ License #

MIT

7
likes
150
points
141
downloads

Documentation

Documentation
API reference

Publisher

verified publisheraayou.sh

Weekly Downloads

A modular in-app developer console for Flutter: logs, network inspector with mocking, crash reports, performance, storage, feature flags, and a plugin system.

Repository (GitHub)
View/report issues

Topics

#debug #logging #network #developer-tools #flutter

License

MIT (license)

Dependencies

bloc, device_info_plus, dio, flutter, flutter_bloc, http, intl, share_plus, shared_preferences

More

Packages that depend on flutter_dev_toolkit