ispect 4.4.0-dev01 copy "ispect: ^4.4.0-dev01" to clipboard
ispect: ^4.4.0-dev01 copied to clipboard

Logging and inspector tool for Flutter development and testing

Logging and inspector tool for Flutter development and testing

pub version License: MIT GitHub stars

Pub likes Pub points

TL;DR #

Drop-in Flutter debug panel: network + logs + performance + UI inspector. Add flag, wrap app, ship safer builds.

Interface Preview #

🏗️ Architecture #

Modular packages. Include only what you use:

Package Role Version
ispect Core panel + inspectors pub
ispectify Logging backbone pub
ispectify_dio Dio HTTP capture pub
ispectify_http http package capture pub
ispectify_ws WebSocket traffic pub
ispectify_bloc BLoC events/states pub
ispect_jira Jira issue export pub

Overview #

ISpect is the main debugging and inspection toolkit designed specifically for Flutter applications.

Provides network, performance, widget tree, logging and device insight tooling via a lightweight in‑app panel.

Key Features #

  • Network Monitoring: Detailed HTTP request/response inspection with error tracking
  • Logging: Advanced logging system with categorization and filtering
  • Performance Analysis: Real-time performance metrics and monitoring
  • UI Inspector: Widget hierarchy inspection with color picker and layout analysis
  • Device Information: System and app metadata collection
  • Bug Reporting: Integrated feedback system with screenshot capture
  • Cache Management: Application cache inspection and management

Logging Configuration #

Core logging powered by ISpectify. Configure via ISpectifyOptions passed to the logger you supply into ISpect.run.

Typical Setup #

final logger = ISpectify(
  options: ISpectifyOptions(
    enabled: true,
    useHistory: true,
    useConsoleLogs: kDebugMode,
    maxHistoryItems: 5000,
    logTruncateLength: 4000,
  ),
);
ISpect.run(() => runApp(App()), logger: logger);

Disable Console Noise #

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

Stateless (No History) #

logger.configure(options: logger.options.copyWith(useHistory: false));

Stream subscribers still receive real-time events.

Filter Example #

class WarningsAndAbove implements ISpectifyFilter {
  @override
  bool apply(ISpectifyData d) => (d.logLevel?.priority ?? 0) >= LogLevel.warning.priority;
}
final logger = ISpectify(filter: WarningsAndAbove());

For advanced knobs (redaction, dynamic reconfigure, zero-allocation tips) see the ISpectify README.

Internationalization #

  • Bundled locales: en, ru, kk, zh, es, fr, de, pt, ar, ko, ja, hi
  • Extend via ISpectLocalizations delegate override

Installation #

Add ispect to your pubspec.yaml:

dependencies:
  ispect: ^4.4.0-dev01

Security & Production Guidelines #

IMPORTANT: ISpect is development‑only. Keep it out of production builds.

Enable with a --dart-define flag. In release without the flag, code is tree‑shaken (no size / perf impact). Wrap all init behind the boolean and avoid committing builds with it enabled.

Full security & environment setup (click to expand)

1. Flag-driven initialization

const bool kEnableISpect = bool.fromEnvironment('ENABLE_ISPECT', defaultValue: false);
void main() {
  if (kEnableISpect) {
    _bootstrapDebug();
  } else {
    runApp(const MyApp());
  }
}
void _bootstrapDebug() {
  final logger = ISpectifyFlutter.init();
  ISpect.run(() => runApp(const MyApp()), logger: logger);
}

2. Build commands

# Dev / QA
flutter run --dart-define=ENABLE_ISPECT=true
# Release (default false)
flutter build apk

3. Verify exclusion Compare sizes: build once with flag true and another without; the delta should reflect removed debug assets.

Benefits

  • Zero production footprint (tree-shaken)
  • Prevents accidental data exposure
  • Faster startup & lower memory in release
  • Clear audit trail via explicit flag

🚀 Quick Start #

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

// Use dart define to control ISpect inclusion
const bool kEnableISpect = bool.fromEnvironment('ENABLE_ISPECT', defaultValue: false);

void main() {
  if (kEnableISpect) {
    // Initialize ISpect only in development/staging
    _initializeISpect();
  } else {
    // Production initialization without ISpect
    runApp(MyApp());
  }
}

void _initializeISpect() {
  // Initialize ISpectify for logging
  final ISpectify logger = ISpectifyFlutter.init();

  // Wrap your app with ISpect
  ISpect.run(
    () => runApp(MyApp()),
    logger: logger,
  );
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: kEnableISpect
          ? ISpectLocalizations.localizationDelegates([
              // Add your localization delegates here
            ])
          : [
              // Your regular localization delegates
            ],
      // Conditionally add ISpectBuilder in MaterialApp builder
      builder: (context, child) {
        if (kEnableISpect) {
          return ISpectBuilder(child: child ?? const SizedBox.shrink());
        }
        return child ?? const SizedBox.shrink();
      },
      home: Scaffold(
        appBar: AppBar(title: const Text('ISpect Example')),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
  ISpect.logger.info('Button pressed!');
            },
            child: const Text('Press me'),
          ),
        ),
      ),
    );
  }
}

Minimal Setup #

// main.dart (minimal enable)
const bool kEnableISpect = bool.fromEnvironment('ENABLE_ISPECT');
void main() {
  if (!kEnableISpect) return runApp(const MyApp());
  final logger = ISpectifyFlutter.init();
  ISpect.run(() => runApp(const MyApp()), logger: logger);
}

// Run with: flutter run --dart-define=ENABLE_ISPECT=true

Advanced Configuration #

Environment-Based Setup #

// Create a dedicated ISpect configuration file
// lib/config/ispect_config.dart

import 'package:flutter/foundation.dart';

class ISpectConfig {
  static const bool isEnabled = bool.fromEnvironment(
    'ENABLE_ISPECT',
    defaultValue: kDebugMode, // Only enable in debug by default
  );
  
  static const String environment = String.fromEnvironment(
    'ENVIRONMENT',
    defaultValue: 'development',
  );
  
  // Only enable in development and staging
  static bool get shouldInitialize => 
    isEnabled && (environment != 'production');
}

Custom Theming (Development Only) #

// Wrap theming configuration in conditional check
Widget build(BuildContext context) {
  return MaterialApp(
    builder: (context, child) {
      if (ISpectConfig.shouldInitialize) {
        return ISpectBuilder(
          theme: ISpectTheme(
            pageTitle: 'Debug Panel',
            lightBackgroundColor: Colors.white,
            darkBackgroundColor: Colors.black,
            lightDividerColor: Colors.grey.shade300,
            darkDividerColor: Colors.grey.shade800,
            logColors: {
              'error': Colors.red,
              'info': Colors.blue,
            },
            logIcons: {
              'error': Icons.error,
              'info': Icons.info,
            },
            logDescriptions: [
              LogDescription(
                key: 'riverpod-add',
                isDisabled: true,
              ),
              LogDescription(
                key: 'riverpod-update',
                isDisabled: true,
              ),
              LogDescription(
                key: 'riverpod-dispose',
                isDisabled: true,
              ),
              LogDescription(
                key: 'riverpod-fail',
                isDisabled: true,
              ),
            ],
          ),
          child: child ?? const SizedBox.shrink(),
        );
      }
      return child ?? const SizedBox.shrink();
    },
    home: Scaffold(/* your app content */),
  );
}

Panel Customization (Development Only) #

Widget build(BuildContext context) {
  return MaterialApp(
    builder: (context, child) {
      if (!ISpectConfig.shouldInitialize) {
        return child ?? const SizedBox.shrink(); // Return app without ISpect in production
      }
      
      return ISpectBuilder(
        options: ISpectOptions(
          locale: const Locale('en'),
          isFeedbackEnabled: true,
          actionItems: [
            ISpectActionItem(
                onTap: (BuildContext context) {
                  // Development-only actions
                },
                title: 'Dev Action',
                icon: Icons.build),
          ],
          panelItems: [
            ISpectPanelItem(
              enableBadge: false,
              icon: Icons.settings,
              onTap: (context) {
                // Handle settings tap
              },
            ),
          ],
          panelButtons: [
            ISpectPanelButtonItem(
                icon: Icons.info,
                label: 'Debug Info',
                onTap: (context) {
                  // Handle debug info tap
                }),
          ],
        ),
        child: child ?? const SizedBox.shrink(),
      );
    },
    home: Scaffold(/* your app content */),
  );
}

Build Configuration Examples #

# Development with ISpect
flutter run --dart-define=ENABLE_ISPECT=true --dart-define=ENVIRONMENT=development

# Staging with ISpect
flutter build apk --dart-define=ENABLE_ISPECT=true --dart-define=ENVIRONMENT=staging

# Production without ISpect (recommended)
flutter build apk --dart-define=ENABLE_ISPECT=false --dart-define=ENVIRONMENT=production

# Or use flavor-specific configurations
flutter build apk --flavor production # ISpect automatically disabled

Integration Guides #

ISpect integrates with various Flutter packages through companion packages. Below are guides for integrating ISpect with HTTP clients, state management, WebSocket connections, and navigation.

Required Dependencies #

Add the following packages to your pubspec.yaml based on your needs:

dependencies:
  # Core ISpect
  ispect: ^4.4.0-dev01
  
  # HTTP integrations (choose one or both)
  ispectify_dio: ^4.4.0-dev01      # For Dio HTTP client
  ispectify_http: ^4.4.0-dev01     # For standard HTTP package
  
  # WebSocket integration
  ispectify_ws: ^4.4.0-dev01       # For WebSocket monitoring
  
  # State management integration
  ispectify_bloc: ^4.4.0-dev01     # For BLoC state management
  
  # Optional: Jira integration
  ispect_jira: ^4.4.0-dev01        # For automated bug reporting

HTTP Integration #

Dio HTTP Client

For Dio integration, use the ispectify_dio package:

dependencies:
  ispectify_dio: ^4.4.0-dev01
import 'package:dio/dio.dart';
import 'package:ispectify_dio/ispectify_dio.dart';

final Dio dio = Dio(
  BaseOptions(
    baseUrl: 'https://api.example.com',
  ),
);

ISpect.run(
  () => runApp(MyApp()),
  logger: iSpectify,
  onInit: () {
    dio.interceptors.add(
      ISpectDioInterceptor(
        logger: iSpectify,
        settings: const ISpectDioInterceptorSettings(
          printRequestHeaders: true,
          printResponseHeaders: true,
          printRequestData: true,
          printResponseData: true,
        ),
      ),
    );
    // Avoid also adding Dio's LogInterceptor unless deliberately comparing outputs.
  },
);

Standard HTTP Client

For standard HTTP package integration, use the ispectify_http package:

dependencies:
  ispectify_http: ^4.4.0-dev01
import 'package:http_interceptor/http_interceptor.dart' as http_interceptor;
import 'package:ispectify_http/ispectify_http.dart';

final http_interceptor.InterceptedClient client =
    http_interceptor.InterceptedClient.build(interceptors: []);

ISpect.run(
  () => runApp(MyApp()),
  logger: iSpectify,
  onInit: () {
    client.interceptors.add(
      ISpectHttpInterceptor(
        logger: iSpectify,
        settings: const ISpectHttpInterceptorSettings(
          printRequestHeaders: true,
          printResponseHeaders: true,
        ),
      ),
    );
  },
);

Multiple HTTP Clients

You can monitor multiple Dio or HTTP clients simultaneously. Placing interceptor setup inside onInit ensures all code is removed from production when the flag is false:

final Dio mainDio = Dio(BaseOptions(baseUrl: 'https://api.example.com'));
final Dio uploadDio = Dio(BaseOptions(baseUrl: 'https://upload.example.com'));

ISpect.run(
  () => runApp(MyApp()),
  logger: iSpectify,
  onInit: () {
    mainDio.interceptors.add(ISpectDioInterceptor(logger: iSpectify));
    uploadDio.interceptors.add(ISpectDioInterceptor(logger: iSpectify));
  },
);

WebSocket Integration #

For WebSocket monitoring, use the ispectify_ws package:

dependencies:
  ispectify_ws: ^4.4.0-dev01
import 'package:ws/ws.dart';
import 'package:ispectify_ws/ispectify_ws.dart';

final interceptor = ISpectWSInterceptor(
  logger: iSpectify,
  settings: const ISpectWSInterceptorSettings(
    enabled: true,
    printSentData: true,
    printReceivedData: true,
    printReceivedMessage: true,
    printErrorData: true,
    printErrorMessage: true,
  ),
);

final client = WebSocketClient(
  WebSocketOptions.common(
    interceptors: [interceptor],
  ),
);

interceptor.setClient(client);

BLoC State Management Integration #

For BLoC integration, use the ispectify_bloc package:

dependencies:
  ispectify_bloc: ^4.4.0-dev01
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:ispectify_bloc/ispectify_bloc.dart';

ISpect.run(
  () => runApp(MyApp()),
  logger: iSpectify,
  onInit: () {
    Bloc.observer = ISpecBlocObserver(
      logger: iSpectify,
    );
  },
);

You can also filter specific BLoC logs in the ISpect theme:

ISpectBuilder(
  theme: const ISpectTheme(
    logDescriptions: [
      LogDescription(
        key: 'bloc-event',
        isDisabled: true,
      ),
      LogDescription(
        key: 'bloc-transition',
        isDisabled: true,
      ),
      LogDescription(
        key: 'bloc-state',
        isDisabled: true,
      ),
    ],
  ),
  child: child,
)

To track screen navigation, use ISpectNavigatorObserver:

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

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

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: (context, child) {
        return ISpectBuilder(
          observer: _observer,
          child: child ?? const SizedBox(),
        );
      },
    );
  }
}

Navigation events will be logged with the key route.

Sensitive Data Redaction #

All integration packages support redaction. Prefer disabling only with synthetic data. Use placeholder values when demonstrating secrets.

Dio Example

final interceptor = ISpectDioInterceptor(
  logger: iSpectify,
  settings: const ISpectDioInterceptorSettings(
    enableRedaction: false, // Only if data is guaranteed non-sensitive
  ),
);

HTTP Example

final redactor = RedactionService();
redactor.ignoreKeys(['authorization', 'x-api-key']);
redactor.ignoreValues(['<placeholder-secret>']);
client.interceptors.add(ISpectHttpInterceptor(logger: iSpectify, redactor: redactor));

WebSocket Example

final redactor = RedactionService();
redactor.ignoreKeys(['auth_token']);
redactor.ignoreValues(['<placeholder>']);
final interceptor = ISpectWSInterceptor(logger: iSpectify, redactor: redactor);

Redaction masks data in headers, bodies, WS messages, and query parameters. Avoid embedding real secrets in code.

Log Filtering and Customization #

ISpectBuilder(
  theme: const ISpectTheme(
    logDescriptions: [
      LogDescription(key: 'bloc-event', isDisabled: true),
      LogDescription(key: 'bloc-transition', isDisabled: true),
      LogDescription(key: 'bloc-state', isDisabled: true),
      LogDescription(key: 'bloc-create', isDisabled: false),
      LogDescription(key: 'bloc-close', isDisabled: false),
      LogDescription(key: 'http-request', isDisabled: false),
      LogDescription(key: 'http-response', isDisabled: false),
      LogDescription(key: 'http-error', isDisabled: false),
      LogDescription(key: 'route', isDisabled: false),
      LogDescription(key: 'print', isDisabled: true),
      LogDescription(key: 'analytics', isDisabled: true),
    ],
  ),
  child: child,
)

Available log keys: bloc-*, http-*, route, print, analytics, error, debug, info

Examples #

Complete example applications are available in the example/ directory demonstrating core functionality.

🤝 Contributing #

Contributions are welcome! Please read our contributing guidelines and submit pull requests to the main branch.

📄 License #

This project is licensed under the MIT License - see the LICENSE file for details.


Built with ❤️ for the Flutter community