ispect 5.0.0-dev23 copy "ispect: ^5.0.0-dev23" to clipboard
ispect: ^5.0.0-dev23 copied to clipboard

Logging and inspector tool for Flutter development and testing

ISpect is a production-safe debugging toolkit for Flutter. It provides a visual debug panel, structured logging, network monitoring, and data redaction — all automatically stripped from release builds via compile-time tree-shaking.

Live web demo — drag and drop exported log files to explore them in the browser.

What's in the box #

Capability What it does
Zero-footprint builds Compile-time const guard removes all code from release APK/IPA
Draggable debug panel Floating panel with quick actions, custom items, and badge notifications
Visual inspector Tap any widget to see its render box, padding, constraints, and colours
Structured logs Typed log entries with levels, filtering, export/import, and session history
Observer hooks Forward log events to Sentry, Crashlytics, or any backend in real time
12 languages en, ru, kk, zh, es, fr, de, pt, ar, ko, ja, hi

For Dio/http/WS/DB/BLoC capture and the standalone layout inspector, see the toolkit packages.

Install #

dependencies:
  ispect: ^5.0.0-dev23

Quick start #

import 'package:flutter/material.dart';
import 'package:ispect/ispect.dart';

void main() {
  ISpect.run(() => runApp(const MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: ISpectLocalizations.delegates(),
      navigatorObservers: ISpectNavigatorObserver.observers(),
      builder: (_, child) => ISpectBuilder.wrap(child: child!),
      home: const HomePage(),
    );
  }
}
# Development — toolkit active.
flutter run --dart-define=ISPECT_ENABLED=true

# Release — toolkit removed via tree-shaking.
flutter build apk

Production safety #

ISpect is flag-gated — with zero footprint in release builds when ISPECT_ENABLED is not defined at compile time. ISpect.run(), ISpectBuilder, and ISpectLocalizations.delegates() become const-guarded no-ops and Dart's tree-shaker eliminates the entire toolkit.

# Development — toolkit active.
flutter run --dart-define=ISPECT_ENABLED=true

# Release — toolkit removed by the tree-shaker.
flutter build apk

For environment-aware control:

import 'package:flutter/foundation.dart';

class ISpectConfig {
  static const bool isEnabled = bool.fromEnvironment(
    'ISPECT_ENABLED',
    defaultValue: kDebugMode,
  );

  static const String environment = String.fromEnvironment(
    'ENVIRONMENT',
    defaultValue: 'development',
  );

  static bool get shouldInitialize => isEnabled && environment != 'production';
}

Measured impact on an obfuscated release APK (no --dart-define=ISPECT_ENABLED): 6 residual "ispect" strings vs. 276 in a development build. See ispect on pub.dev for the full production-safety guide.

Logger configuration #

void main() {
  final logger = ISpectFlutter.init(
    options: ISpectLoggerOptions(
      enabled: true,
      useHistory: true,
      useConsoleLogs: kDebugMode,
      maxHistoryItems: 5000,
      logTruncateLength: 4000,
    ),
  );

  ISpect.run(logger: logger, () => runApp(const MyApp()));
}

Disable console output (logs still flow to observers and the UI):

ISpect.logger.configure(
  options: ISpect.logger.options.copyWith(useConsoleLogs: false),
);

Streaming-only (no in-memory history, useful for observer-driven pipelines):

final logger = ISpectFlutter.init(
  options: const ISpectLoggerOptions(useHistory: false),
);

Filter by log-type key (suppress noisy categories without touching call sites):

final logger = ISpectFlutter.init(
  filter: ISpectFilter(logTypeKeys: {'analytics', 'route'}),
);

Filter by severity (drop debug/verbose, keep info and above):

final logger = ISpectFlutter.init(
  logger: ISpectBaseLogger(
    filter: LogLevelRangeFilter(minLevel: LogLevel.info),
  ),
);

Localization #

ISpect ships with 12 built-in locales. ISpectLocalizations.delegates() merges ISpect's translations with your own delegates in a single call:

MaterialApp(
  localizationsDelegates: ISpectLocalizations.delegates(
    delegates: [
      // your app's delegates go here
    ],
  ),
)

Force a specific locale regardless of the app locale:

ISpectBuilder(
  options: ISpectOptions(
    observer: observer,
    locale: const Locale('ru'),
  ),
  child: child ?? const SizedBox.shrink(),
)

Observers #

Observers tap into the log stream without coupling your app to ISpect internals — use them to bridge events to any external service.

class SentryISpectObserver extends ISpectObserver {
  const SentryISpectObserver();

  @override
  void onLog(ISpectLogData data) {
    // Add as Sentry breadcrumb.
  }

  @override
  void onError(ISpectLogData err) {
    // Sentry.captureException(err.exception, stackTrace: err.stackTrace);
  }

  @override
  void onException(ISpectLogData err) {
    // Sentry.captureException(err.exception, stackTrace: err.stackTrace);
  }
}

void main() {
  final logger = ISpectFlutter.init();
  logger.addObserver(const SentryISpectObserver());

  ISpect.run(logger: logger, () => runApp(const MyApp()));
}

Theming #

ISpectBuilder(
  options: ISpectOptions(
    observer: observer,
    initialSettings: ISpectSettingsState(
      disabledLogTypes: {
        'riverpod-add',
        'riverpod-update',
        'riverpod-dispose',
        'riverpod-fail',
      },
    ),
  ),
  theme: ISpectTheme(
    pageTitle: 'Debug Panel',
    background: ISpectDynamicColor(light: Colors.white, dark: Colors.black),
    divider: ISpectDynamicColor(
      light: Colors.grey.shade300,
      dark: Colors.grey.shade800,
    ),
    logColors: {
      'error': Colors.red,
      'warning': Colors.orange,
      'info': Colors.blue,
      'debug': Colors.grey,
    },
    logIcons: {
      'error': Icons.error,
      'warning': Icons.warning,
      'info': Icons.info,
      'debug': Icons.bug_report,
    },
    logDescriptions: {
      'error': 'Critical application errors',
      'info': 'Informational messages',
    },
  ),
  child: child ?? const SizedBox.shrink(),
)

Panel actions and custom items #

ISpectBuilder(
  options: ISpectOptions(
    observer: observer,
    locale: const Locale('en'),
    actionItems: [
      ISpectActionItem(
        onTap: (context) { /* Clear cache, reset state */ },
        title: 'Clear All Data',
        icon: Icons.delete_sweep,
      ),
    ],
    panelItems: [
      DraggablePanelItem(
        enableBadge: false,
        icon: Icons.settings,
        onTap: (context) { /* Open settings */ },
      ),
    ],
    panelButtons: [
      DraggablePanelButtonItem(
        icon: Icons.info,
        label: 'App Info',
        onTap: (context) { /* Show app version */ },
      ),
    ],
  ),
  child: child ?? const SizedBox.shrink(),
)

Settings persistence #

Load saved settings on startup and persist changes via onSettingsChanged:

import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final prefs = await SharedPreferences.getInstance();
  final settingsJson = prefs.getString('ispect_settings');
  final initialSettings = settingsJson != null
      ? ISpectSettingsState.fromJson(jsonDecode(settingsJson))
      : null;

  final logger = ISpectFlutter.init();
  ISpect.run(logger: logger, () => runApp(MyApp(initialSettings: initialSettings)));
}

ISpectBuilder(
  options: ISpectOptions(
    observer: observer,
    initialSettings: initialSettings ?? const ISpectSettingsState(
      enabled: true,
      useConsoleLogs: true,
      useHistory: true,
    ),
    onSettingsChanged: (settings) async {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('ispect_settings', jsonEncode(settings.toJson()));
    },
  ),
  child: child ?? const SizedBox.shrink(),
)

Callbacks #

ISpectBuilder(
  options: ISpectOptions(
    observer: observer,
    onLoadLogContent: (context) async {
      // Load log files from storage via file_picker.
      return 'Loaded log content';
    },
    onOpenFile: (path) async {
      // Open with the system viewer via open_filex.
    },
    onShare: (ISpectShareRequest request) async {
      // Share via share_plus.
    },
  ),
  child: child ?? const SizedBox.shrink(),
)
class _MyAppState extends State<MyApp> {
  final _observer = ISpectNavigatorObserver(
    isLogModals: true,
    isLogPages: true,
    isLogGestures: false,
    isLogOtherTypes: true,
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorObservers: [_observer],
      builder: (_, child) => ISpectBuilder(
        observer: _observer,
        child: child ?? const SizedBox(),
      ),
    );
  }
}

Security considerations #

Exported log files (share, daily sessions) are stored as plain-text JSON on disk. Network traffic is redacted automatically (see the ispectify_* packages), but messages written via ISpect.logger.info(...) are stored as-is.

  • Never log PII (emails, phone numbers, tokens, passwords) via ISpect.logger.*.
  • Review redaction rules in your network interceptors to cover all sensitive headers and URL parameters.
  • In production builds the toolkit is fully tree-shaken when ISPECT_ENABLED is unset, so no log files are created.
  • For sensitive environments, clear daily sessions regularly or disable file logging.

Example #

See example/ for a complete working app.

The ISpect toolkit #

ISpect is a modular monorepo. Install only what your project needs — each package works independently.

Package What it does
ispect Flutter UI — debug panel, log viewer, navigation observer, inspector integration
ispect_layout Visual layout inspector — sizes, constraints, decorations, compare mode, color picker
ispectify Pure-Dart logging core — typed log entries, filtering, tracing, observers
ispectify_dio Dio HTTP interceptor with automatic redaction
ispectify_http http package interceptor with automatic redaction
ispectify_ws WebSocket traffic capture with automatic redaction
ispectify_db Database operation tracing (SQL, ORM, KV stores)
ispectify_bloc BLoC event / state / transition observer

Contributing #

Contributions are welcome. See CONTRIBUTING.md for guidelines, and open issues or pull requests at the ISpect repository.

License #

MIT — see LICENSE.