zero_inspector_kit 1.0.1 copy "zero_inspector_kit: ^1.0.1" to clipboard
zero_inspector_kit: ^1.0.1 copied to clipboard

A Flutter plugin for in-app developer console with network request viewing, logging, database inspection, and route tracking.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:zero_inspector_kit/zero_inspector_kit.dart';
import 'package:logger/logger.dart';

void main() {
  /// 零侵入集成:仅需一行代码即可启用所有检查器功能
  /// runAppWithInspector 会自动:
  /// 1. 初始化检查器(日志捕获、网络拦截、数据库扫描)
  /// 2. 通过 Zone 捕获所有 print() 输出
  /// 3. 自动显示悬浮按钮
  ZeroInspectorKit.runAppWithInspector(const MyApp());
}

/// 零侵入使用:无需修改任何现有代码
/// 检查器会自动:
/// - 捕获所有 print() 输出和 Flutter 错误
/// - 拦截所有 http 包和 Dio 请求
/// - 扫描 SQLite 数据库
/// - 跟踪路由导航
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorObservers: [InspectorRouteObserver()],
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final Dio _dio = Dio();
  final InspectorDioInterceptor _dioInterceptor = InspectorDioInterceptor();
  late Logger _logger;

  @override
  void initState() {
    super.initState();

    /// 注意:SQLite 数据库提供者已在 ZeroInspectorKit.init() 中自动注册,无需手动注册
    /// DatabaseRegistry.instance.registerProvider(SqliteDatabaseProvider()); // 这行已不需要

    _setupLoggerIntegration();
    _setupDioInterceptor();
    _initTestDatabase();
  }

  void _setupLoggerIntegration() {
    _logger = Logger(printer: PrettyPrinter(methodCount: 0, printEmojis: true));
  }

  void _logWithLogger() {
    _logger.t('Logger trace message');
    _logger.d('Logger debug message');
    _logger.i('Logger info message');
    _logger.w('Logger warning message');
    _logger.e('Logger error message');
    _logger.f('Logger fatal message');
  }

  Future<void> _initTestDatabase() async {
    try {
      final dbPath = await getDatabasesPath();
      await openDatabase(
        join(dbPath, 'test_database.db'),
        version: 1,
        onCreate: (db, version) async {
          await db.execute('''
            CREATE TABLE users (
              id INTEGER PRIMARY KEY AUTOINCREMENT,
              name TEXT,
              email TEXT,
              age INTEGER
            )
          ''');
          await db.execute('''
            CREATE TABLE posts (
              id INTEGER PRIMARY KEY AUTOINCREMENT,
              title TEXT,
              content TEXT,
              user_id INTEGER,
              created_at TEXT
            )
          ''');
          await db.insert('users', {
            'name': 'Alice',
            'email': 'alice@example.com',
            'age': 25,
          });
          await db.insert('users', {
            'name': 'Bob',
            'email': 'bob@example.com',
            'age': 30,
          });
          await db.insert('users', {
            'name': 'Charlie',
            'email': 'charlie@example.com',
            'age': 35,
          });
          await db.insert('posts', {
            'title': 'First Post',
            'content': 'Hello World!',
            'user_id': 1,
            'created_at': '2024-01-01',
          });
          await db.insert('posts', {
            'title': 'Second Post',
            'content': 'Flutter is awesome',
            'user_id': 2,
            'created_at': '2024-01-02',
          });
        },
      );

      await openDatabase(
        join(dbPath, 'test_data.sqlite'),
        version: 1,
        onCreate: (db, version) async {
          await db.execute('''
            CREATE TABLE products (
              id INTEGER PRIMARY KEY AUTOINCREMENT,
              name TEXT,
              price REAL,
              stock INTEGER
            )
          ''');
          await db.execute('''
            CREATE TABLE orders (
              id INTEGER PRIMARY KEY AUTOINCREMENT,
              product_id INTEGER,
              quantity INTEGER,
              total REAL,
              created_at TEXT
            )
          ''');
          await db.insert('products', {
            'name': 'iPhone',
            'price': 5999,
            'stock': 100,
          });
          await db.insert('products', {
            'name': 'iPad',
            'price': 3999,
            'stock': 50,
          });
          await db.insert('products', {
            'name': 'MacBook',
            'price': 12999,
            'stock': 30,
          });
          await db.insert('orders', {
            'product_id': 1,
            'quantity': 2,
            'total': 11998,
            'created_at': '2024-01-03',
          });
          await db.insert('orders', {
            'product_id': 2,
            'quantity': 1,
            'total': 3999,
            'created_at': '2024-01-04',
          });
        },
      );

      print('Test databases initialized successfully');
    } catch (e) {
      print('Failed to initialize databases: $e');
    }
  }

  void _setupDioInterceptor() {
    _dio.interceptors.add(
      InterceptorsWrapper(
        onRequest: (options, handler) {
          _dioInterceptor.onRequest({
            'method': options.method,
            'url': options.uri.toString(),
            'headers': options.headers.cast<String, String>(),
            'data': options.data,
          });
          handler.next(options);
        },
        onResponse: (response, handler) {
          _dioInterceptor.onResponse({
            'data': response.data,
            'statusCode': response.statusCode,
            'requestOptions': {
              'method': response.requestOptions.method,
              'uri': response.requestOptions.uri,
            },
          });
          handler.next(response);
        },
        onError: (error, handler) {
          _dioInterceptor.onError({
            'message': error.message,
            'response': error.response != null
                ? {
                    'data': error.response!.data,
                    'statusCode': error.response!.statusCode,
                  }
                : null,
            'requestOptions': {
              'method': error.requestOptions.method,
              'uri': error.requestOptions.uri,
            },
          });
          handler.next(error);
        },
      ),
    );
  }

  Future<void> _makeDioRequest() async {
    try {
      await _dio.get('https://jsonplaceholder.typicode.com/posts/1');
      print('Dio request completed successfully');
    } catch (e) {
      print('Dio request failed: $e');
    }
  }

  Future<void> _makeHttpGetRequest() async {
    try {
      await InspectorHttpInterceptor.instance.get(
        Uri.parse('https://jsonplaceholder.typicode.com/posts/2'),
      );
      print('HTTP GET request completed successfully');
    } catch (e) {
      print('HTTP GET request failed: $e');
    }
  }

  Future<void> _makeHttpPostRequest() async {
    try {
      await InspectorHttpInterceptor.instance.post(
        Uri.parse('https://jsonplaceholder.typicode.com/posts'),
        body: {'title': 'foo', 'body': 'bar', 'userId': 1},
      );
      print('HTTP POST request completed successfully');
    } catch (e) {
      print('HTTP POST request failed: $e');
    }
  }

  void _logMessages() {
    print('[VERBOSE] This is a verbose log');
    print('[DEBUG] This is a debug log');
    print('[INFO] This is an info log');
    print('[WARNING] This is a warning log');
    print('[ERROR] This is an error log');
  }

  void _logWithCustomCallback() {
    InspectorLogInterceptor.instance.debug('Custom debug log via callback');
    InspectorLogInterceptor.instance.info('Custom info log via callback');
    InspectorLogInterceptor.instance.warning('Custom warning log via callback');
    InspectorLogInterceptor.instance.error('Custom error log via callback');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Zero Inspector Kit Example')),
      body: Center(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(16),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text(
                'Network Requests',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 12),
              ElevatedButton(
                onPressed: _makeDioRequest,
                child: const Text('Dio GET Request'),
              ),
              const SizedBox(height: 8),
              ElevatedButton(
                onPressed: _makeHttpGetRequest,
                child: const Text('HTTP GET Request'),
              ),
              const SizedBox(height: 8),
              ElevatedButton(
                onPressed: _makeHttpPostRequest,
                child: const Text('HTTP POST Request'),
              ),
              const SizedBox(height: 24),
              const Text(
                'Logs',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 12),
              ElevatedButton(
                onPressed: _logMessages,
                child: const Text('Generate Logs'),
              ),
              const SizedBox(height: 8),
              ElevatedButton(
                onPressed: _logWithCustomCallback,
                child: const Text('Custom Logs (Callback)'),
              ),
              const SizedBox(height: 8),
              ElevatedButton(
                onPressed: _logWithLogger,
                child: const Text('Logger Library Logs'),
              ),
              const SizedBox(height: 8),
              const Text(
                'Logs are automatically captured from:\n- print() calls\n- Flutter errors/exceptions\n- Custom log methods\n- Logger library (via integration)',
                textAlign: TextAlign.center,
                style: TextStyle(color: Colors.grey, fontSize: 14),
              ),
              const SizedBox(height: 8),
              const Text(
                'Production Build: Inspector is automatically\n'
                'disabled in release mode (no code changes needed)',
                textAlign: TextAlign.center,
                style: TextStyle(color: Colors.blue, fontSize: 12),
              ),
              const SizedBox(height: 8),
              const Text(
                'Third-party log library integration:\n'
                'Logger logs are captured because print() is called\n'
                'Alternatively, use onLogCaptured callback for custom handling',
                textAlign: TextAlign.center,
                style: TextStyle(color: Colors.grey, fontSize: 12),
              ),
              const SizedBox(height: 24),
              const Text(
                'Database',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 12),
              const Text(
                'Test database with users and posts tables\nis auto-created on app startup',
                textAlign: TextAlign.center,
                style: TextStyle(color: Colors.grey, fontSize: 14),
              ),
              const SizedBox(height: 24),
              const Text(
                'Navigation',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 12),
              ElevatedButton(
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => const SecondScreen(),
                    ),
                  );
                },
                child: const Text('Navigate to Second Screen'),
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: const FloatingInspectorButton(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Second Screen')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('This is the second screen'),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) => const ThirdScreen()),
                );
              },
              child: const Text('Navigate to Third Screen'),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text('Go Back'),
            ),
          ],
        ),
      ),
      floatingActionButton: const FloatingInspectorButton(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Third Screen')),
      body: const Center(child: Text('This is the third screen')),
      floatingActionButton: const FloatingInspectorButton(),
    );
  }
}
1
likes
0
points
180
downloads

Publisher

verified publisherzerolabsco.com

Weekly Downloads

A Flutter plugin for in-app developer console with network request viewing, logging, database inspection, and route tracking.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

collection, flutter, http, path_provider, plugin_platform_interface, sqflite

More

Packages that depend on zero_inspector_kit

Packages that implement zero_inspector_kit