backstage 0.0.6 copy "backstage: ^0.0.6" to clipboard
backstage: ^0.0.6 copied to clipboard

Complete in-app debugging console with advanced logging, network monitoring, performance metrics, and export capabilities. Production-ready with security features.

example/lib/main.dart

import 'dart:async';
import 'dart:math';

import 'package:backstage/backstage.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

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

  // Initialize Backstage with comprehensive configuration
  await Backstage().init(const BackstageConfig(
    // Core logging features
    capturePrint: true,
    captureFlutterErrors: true,
    captureZoneErrors: false, // Simplified for demo - avoid zone complexity
    enabledByDefault: kDebugMode, // Auto-enable in debug mode
    persistEnabled: true,

    // Advanced features
    captureNetworkRequests: true,
    capturePerformanceMetrics: true,
    enableExport: true,
    persistLogs: false, // Keep it simple for demo

    // Security configuration
    requireBiometric: false, // Set to true in production
    securityConfig: SecurityConfig(
      sanitizePatterns: ['password=.*', 'token=.*', 'secret=.*'],
      maxFailedAttempts: 3,
    ),

    // Network monitoring configuration
    networkConfig: NetworkConfig(
      captureHeaders: true,
      captureErrors: true,
      maxBodySize: 1024 * 10, // 10KB max body size
    ),

    // Performance monitoring configuration - optimized for demo to reduce noise
    performanceConfig: PerformanceConfig(
      trackMemoryUsage: true,
      trackFPS: true,
      trackCPUUsage: false, // Disabled for demo to reduce overhead
      trackBatteryStatus: false, // Disabled for demo
      trackConnectivity: false, // Disabled for demo
      collectionIntervalMs: 5000, // Collect less frequently (every 5 seconds)
      frameTimeWarningThresholdMs: 100, // Very lenient threshold (100ms = 10 FPS)
      memoryWarningThresholdMB: 300, // High threshold to reduce noise
      memoryCriticalThresholdMB: 500, // Very high threshold
      maxDataPoints: 50, // Limit data retention for demo
    ),

    // Export configuration
    exportConfig: ExportConfig(
      allowSharing: true,
      includeMetadata: true,
      includeSystemInfo: kDebugMode, // Only in debug mode
      maxEntriesPerExport: 5000,
      compressExports: true,
      autoCleanupExports: true,
      exportRetentionDays: 7,
    ),

    // Storage configuration - using memory for simplicity in demo
    storageConfig: StorageConfig(
      backend: StorageBackend.memory,
      maxLogEntries: 1000,
      retentionDays: 1,
    ),

    // UI configuration
    uiConfig: UIConfig(
      layout: ConsoleLayout.tabbed,
      defaultPosition: DefaultPosition.topRight,
      minPanelWidth: 350,
      maxPanelWidth: 700,
      isResizable: true,
      enableAnimations: true,
      showPerformanceOverlay: kDebugMode,
      panelElevation: 8.0,
      borderRadius: 12.0,
      panelOpacity: 0.95,
    ),

    // Theme configuration
    theme: BackstageTheme.system,
    themeConfig: ThemeConfig(
      primaryColor: Colors.blue,
      fontFamily: 'monospace',
    ),
  ));

  // Run app - zone error capture is handled internally by Backstage
  runApp(const BackstageExampleApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Backstage Feature Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: BackstageOverlay(
        child: BackstageEntryGate(
          passcode: kDebugMode ? null : '1234',
          child: const FeatureDemoHome(),
        ),
      ),
    );
  }
}

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

  @override
  State<FeatureDemoHome> createState() => _FeatureDemoHomeState();
}

class _FeatureDemoHomeState extends State<FeatureDemoHome>
    with TickerProviderStateMixin {
  final _dio = Dio();
  Timer? _performanceTimer;
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 6, vsync: this);
    _setupNetworkInterceptor();
    _startPerformanceDemo();
  }

  @override
  void dispose() {
    _performanceTimer?.cancel();
    _tabController.dispose();
    super.dispose();
  }

  void _setupNetworkInterceptor() {
    // Network requests will be automatically captured by Backstage
    // No manual interceptor setup needed
  }

  void _startPerformanceDemo() {
    // Simulate some performance activity
    _performanceTimer = Timer.periodic(const Duration(seconds: 10), (_) {
      _simulatePerformanceActivity();
    });
  }

  void _simulatePerformanceActivity() {
    // Simulate some work to generate performance data
    final random = Random();
    final list = List.generate(random.nextInt(1000) + 100, (i) => i * i);
    list.sort();

    // Log performance activity
    Backstage.I.logger.d(
      'Simulated performance activity: processed ${list.length} items',
      tag: 'performance',
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Backstage Feature Demo'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        bottom: TabBar(
          controller: _tabController,
          isScrollable: true,
          tabs: const [
            Tab(icon: Icon(Icons.bug_report), text: 'Logging'),
            Tab(icon: Icon(Icons.network_check), text: 'Network'),
            Tab(icon: Icon(Icons.speed), text: 'Performance'),
            Tab(icon: Icon(Icons.security), text: 'Security'),
            Tab(icon: Icon(Icons.share), text: 'Export'),
            Tab(icon: Icon(Icons.info), text: 'About'),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: [
          _buildLoggingDemo(),
          _buildNetworkDemo(),
          _buildPerformanceDemo(),
          _buildSecurityDemo(),
          _buildExportDemo(),
          _buildAboutTab(),
        ],
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () => Backstage.I.setEnabled(true),
        icon: const Icon(Icons.developer_mode),
        label: const Text('Open Backstage'),
        backgroundColor: Colors.green,
      ),
    );
  }

  Widget _buildLoggingDemo() {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        const Text(
          'Logging Features Demo',
          style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 16),
        const Text(
          'Tap the activation area below 5 times or long-press to open Backstage console:',
        ),
        const SizedBox(height: 16),
        Container(
          height: 80,
          decoration: BoxDecoration(
            border: Border.all(color: Colors.blue, width: 2),
            borderRadius: BorderRadius.circular(8),
          ),
          child: const Center(
            child: Text(
              '🎯 TAP HERE 5Γ— OR LONG-PRESS\nto activate Backstage',
              textAlign: TextAlign.center,
              style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
            ),
          ),
        ),
        const SizedBox(height: 24),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            ElevatedButton.icon(
              onPressed: _emitSampleLogs,
              icon: const Icon(Icons.article),
              label: const Text('Sample Logs'),
            ),
            ElevatedButton.icon(
              onPressed: _emitErrorWithStackTrace,
              icon: const Icon(Icons.error),
              label: const Text('Error + Stack'),
            ),
            ElevatedButton.icon(
              onPressed: _emitPrintMessages,
              icon: const Icon(Icons.print),
              label: const Text('Print Messages'),
            ),
            ElevatedButton.icon(
              onPressed: _triggerFlutterError,
              icon: const Icon(Icons.warning),
              label: const Text('Flutter Error'),
            ),
            ElevatedButton.icon(
              onPressed: _triggerAsyncError,
              icon: const Icon(Icons.timer),
              label: const Text('Async Error (Demo)'),
            ),
          ],
        ),
      ],
    );
  }

  Widget _buildNetworkDemo() {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        const Text(
          'Network Monitoring Demo',
          style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 16),
        const Text(
          'These buttons will make HTTP requests that are automatically captured by Backstage:',
        ),
        const SizedBox(height: 16),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            ElevatedButton.icon(
              onPressed: _makeSuccessfulRequest,
              icon: const Icon(Icons.check_circle),
              label: const Text('Successful GET'),
            ),
            ElevatedButton.icon(
              onPressed: _makePostRequest,
              icon: const Icon(Icons.send),
              label: const Text('POST with Body'),
            ),
            ElevatedButton.icon(
              onPressed: _makeFailedRequest,
              icon: const Icon(Icons.error),
              label: const Text('Failed Request'),
            ),
            ElevatedButton.icon(
              onPressed: _makeSlowRequest,
              icon: const Icon(Icons.access_time),
              label: const Text('Slow Request'),
            ),
            ElevatedButton.icon(
              onPressed: _makeMultipleRequests,
              icon: const Icon(Icons.multiple_stop),
              label: const Text('Multiple Requests'),
            ),
          ],
        ),
      ],
    );
  }

  Widget _buildPerformanceDemo() {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        const Text(
          'Performance Monitoring Demo',
          style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 16),
        Card(
          color: Colors.blue.shade50,
          child: const Padding(
            padding: EdgeInsets.all(12),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'πŸ“ˆ Performance Noise Reduction',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
                SizedBox(height: 4),
                Text(
                  'This demo uses throttled performance monitoring:\n'
                  'β€’ Frame warnings limited to every 5 seconds\n'
                  'β€’ Lenient thresholds (100ms = 10 FPS)\n'
                  'β€’ Reduced collection frequency (5s intervals)\n'
                  'β€’ Aggregated janky frame reporting\n'
                  'β€’ Optional: Set trackFPS: false to disable entirely',
                  style: TextStyle(fontSize: 12),
                ),
              ],
            ),
          ),
        ),
        const SizedBox(height: 16),
        const Text(
          'Performance metrics are automatically collected. These buttons simulate various performance scenarios:',
        ),
        const SizedBox(height: 16),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            ElevatedButton.icon(
              onPressed: _simulateMemoryUsage,
              icon: const Icon(Icons.memory),
              label: const Text('Memory Usage'),
            ),
            ElevatedButton.icon(
              onPressed: _simulateHeavyComputation,
              icon: const Icon(Icons.calculate),
              label: const Text('Heavy Computation'),
            ),
            ElevatedButton.icon(
              onPressed: _simulateFrameDrops,
              icon: const Icon(Icons.videocam_off),
              label: const Text('Frame Drops'),
            ),
            ElevatedButton.icon(
              onPressed: _checkBatteryStatus,
              icon: const Icon(Icons.battery_std),
              label: const Text('Battery Info'),
            ),
          ],
        ),
      ],
    );
  }

  Widget _buildSecurityDemo() {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        const Text(
          'Security Features Demo',
          style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 16),
        const Text(
          'Security features include data sanitization and access control:',
        ),
        const SizedBox(height: 16),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            ElevatedButton.icon(
              onPressed: _logSensitiveData,
              icon: const Icon(Icons.security),
              label: const Text('Sensitive Data (Sanitized)'),
            ),
            ElevatedButton.icon(
              onPressed: _testAccessControl,
              icon: const Icon(Icons.lock),
              label: const Text('Access Control'),
            ),
            ElevatedButton.icon(
              onPressed: _logSecurityEvent,
              icon: const Icon(Icons.shield),
              label: const Text('Security Event'),
            ),
          ],
        ),
      ],
    );
  }

  Widget _buildExportDemo() {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        const Text(
          'Export & Sharing Demo',
          style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 16),
        const Text(
          'Export logs in various formats and share them:',
        ),
        const SizedBox(height: 16),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            ElevatedButton.icon(
              onPressed: () => _exportLogs('json'),
              icon: const Icon(Icons.code),
              label: const Text('Export JSON'),
            ),
            ElevatedButton.icon(
              onPressed: () => _exportLogs('csv'),
              icon: const Icon(Icons.table_chart),
              label: const Text('Export CSV'),
            ),
            ElevatedButton.icon(
              onPressed: () => _exportLogs('html'),
              icon: const Icon(Icons.web),
              label: const Text('Export HTML'),
            ),
            ElevatedButton.icon(
              onPressed: () => _exportLogs('text'),
              icon: const Icon(Icons.text_fields),
              label: const Text('Export Text'),
            ),
          ],
        ),
      ],
    );
  }

  Widget _buildAboutTab() {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        const Text(
          'About Backstage',
          style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        ),
        const SizedBox(height: 16),
        Card(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  'Features Enabled:',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 8),
                _buildFeatureStatus('Core Logging', true),
                _buildFeatureStatus(
                    'Network Monitoring', Backstage.I.networkService != null),
                _buildFeatureStatus('Performance Metrics',
                    Backstage.I.performanceService != null),
                _buildFeatureStatus(
                    'Security Features', Backstage.I.securityService != null),
                _buildFeatureStatus(
                    'Export Capability', Backstage.I.exportService != null),
                _buildFeatureStatus(
                    'Enhanced Logger', Backstage.I.enhancedLogger != null),
              ],
            ),
          ),
        ),
        const SizedBox(height: 16),
        Card(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  'How to Use:',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 8),
                const Text('1. Tap the activation area 5 times or long-press'),
                const Text(
                    '2. Enter passcode if required (1234 in release mode)'),
                const Text(
                    '3. Browse tabs: Logs, Network, Performance, Export'),
                const Text(
                    '4. Use search and filters to find specific entries'),
                const Text('5. Export data in various formats for analysis'),
              ],
            ),
          ),
        ),
        const SizedBox(height: 16),
        ElevatedButton.icon(
          onPressed: _showSystemInfo,
          icon: const Icon(Icons.info),
          label: const Text('Show System Info'),
        ),
      ],
    );
  }

  Widget _buildFeatureStatus(String feature, bool enabled) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 2),
      child: Row(
        children: [
          Icon(
            enabled ? Icons.check_circle : Icons.cancel,
            color: enabled ? Colors.green : Colors.red,
            size: 16,
          ),
          const SizedBox(width: 8),
          Text(feature),
        ],
      ),
    );
  }

  // Logging demo methods
  void _emitSampleLogs() {
    final logger = Backstage.I.logger;
    logger.d('Debug: This is a debug message', tag: 'demo');
    logger.i('Info: Application started successfully', tag: 'demo');
    logger.w('Warning: This is a warning message', tag: 'demo');
    logger.e('Error: Something went wrong', tag: 'demo');
  }

  void _emitErrorWithStackTrace() {
    try {
      throw Exception('Intentional demo exception for stack trace');
    } catch (e, stackTrace) {
      Backstage.I.logger.e(
        'Caught exception: $e',
        tag: 'error_demo',
        st: stackTrace,
      );
    }
  }

  void _emitPrintMessages() {
    print('This is a regular print message');
    debugPrint('This is a debugPrint message');
  }

  void _triggerFlutterError() {
    // Trigger a Flutter framework error
    WidgetsBinding.instance.addPostFrameCallback((_) {
      throw FlutterError('Demo Flutter framework error');
    });
  }

  void _triggerAsyncError() {
    // Demonstrate how async errors would be handled
    // Note: Zone error capture is disabled in this demo for simplicity
    Backstage.I.logger.e(
      'Demo: This would be an async error caught by zone error capture',
      tag: 'async_demo',
    );
  }

  // Network demo methods
  Future<void> _makeSuccessfulRequest() async {
    try {
      final response =
          await _dio.get('https://jsonplaceholder.typicode.com/posts/1');
      Backstage.I.logger
          .i('Successful request: ${response.statusCode}', tag: 'network');
    } catch (e) {
      Backstage.I.logger.e('Request failed: $e', tag: 'network');
    }
  }

  Future<void> _makePostRequest() async {
    try {
      final data = {
        'title': 'Backstage Demo Post',
        'body': 'This is a demo POST request from Backstage',
        'userId': 1,
      };
      final response = await _dio.post(
        'https://jsonplaceholder.typicode.com/posts',
        data: data,
      );
      Backstage.I.logger
          .i('POST request: ${response.statusCode}', tag: 'network');
    } catch (e) {
      Backstage.I.logger.e('POST request failed: $e', tag: 'network');
    }
  }

  Future<void> _makeFailedRequest() async {
    try {
      await _dio.get('https://httpstat.us/404');
    } catch (e) {
      Backstage.I.logger.e('Expected 404 error: $e', tag: 'network');
    }
  }

  Future<void> _makeSlowRequest() async {
    try {
      final response = await _dio.get('https://httpstat.us/200?sleep=3000');
      Backstage.I.logger
          .i('Slow request completed: ${response.statusCode}', tag: 'network');
    } catch (e) {
      Backstage.I.logger.e('Slow request failed: $e', tag: 'network');
    }
  }

  Future<void> _makeMultipleRequests() async {
    final futures = List.generate(5, (i) async {
      try {
        final response = await _dio
            .get('https://jsonplaceholder.typicode.com/posts/${i + 1}');
        Backstage.I.logger
            .d('Request $i completed: ${response.statusCode}', tag: 'network');
      } catch (e) {
        Backstage.I.logger.e('Request $i failed: $e', tag: 'network');
      }
    });

    await Future.wait(futures);
    Backstage.I.logger.i('All multiple requests completed', tag: 'network');
  }

  // Performance demo methods
  void _simulateMemoryUsage() {
    // Simulate memory allocation
    final largeList = List.generate(100000, (i) => 'Item $i ' * 10);
    Backstage.I.logger.i(
      'Simulated memory usage: ${largeList.length} items allocated',
      tag: 'performance',
    );
    // Let GC clean up
    largeList.clear();
  }

  void _simulateHeavyComputation() {
    final stopwatch = Stopwatch()..start();

    // Heavy computation
    var result = 0;
    for (int i = 0; i < 1000000; i++) {
      result += i * i;
    }

    stopwatch.stop();
    Backstage.I.logger.i(
      'Heavy computation completed in ${stopwatch.elapsedMilliseconds}ms (result: $result)',
      tag: 'performance',
    );
  }

  void _simulateFrameDrops() {
    // Simulate frame drops by doing heavy work on main thread
    final stopwatch = Stopwatch()..start();
    while (stopwatch.elapsedMilliseconds < 100) {
      // Busy wait to block main thread
    }
    Backstage.I.logger
        .w('Simulated frame drops (100ms block)', tag: 'performance');
  }

  void _checkBatteryStatus() {
    Backstage.I.logger.i('Battery status check requested', tag: 'performance');
    // The actual battery info will be collected by the performance service
  }

  // Security demo methods
  void _logSensitiveData() {
    // This data should be sanitized according to the security config
    Backstage.I.logger.w(
      'User login attempt: password=secret123 token=abc123xyz secret=confidential',
      tag: 'security',
    );
    Backstage.I.logger.i(
      'Login successful for user: john.doe@example.com',
      tag: 'security',
    );
  }

  void _testAccessControl() {
    Backstage.I.logger
        .i('Access control test: checking permissions', tag: 'security');
    // Simulate access control check
    final hasAccess = Random().nextBool();
    if (hasAccess) {
      Backstage.I.logger.i('Access granted to admin panel', tag: 'security');
    } else {
      Backstage.I.logger
          .w('Access denied: insufficient privileges', tag: 'security');
    }
  }

  void _logSecurityEvent() {
    final events = [
      'Suspicious login attempt from unknown device',
      'Rate limit exceeded for API endpoint',
      'Invalid token provided in request',
      'Potential XSS attempt detected',
      'Failed biometric authentication',
    ];
    final event = events[Random().nextInt(events.length)];
    Backstage.I.logger.e('Security event: $event', tag: 'security');
  }

  // Export demo methods
  Future<void> _exportLogs(String formatName) async {
    if (Backstage.I.exportService == null) {
      Backstage.I.logger.w('Export service not available', tag: 'export');
      return;
    }

    try {
      Backstage.I.logger.i(
        'Export functionality demo - would export logs in $formatName format',
        tag: 'export',
      );

      // Note: Actual export functionality would use the console's built-in export features
      // This is just a demo of the logging capabilities
    } catch (e) {
      Backstage.I.logger.e('Export demo failed: $e', tag: 'export');
    }
  }

  void _showSystemInfo() {
    final info = {
      'Platform': Theme.of(context).platform.name,
      'Debug Mode': kDebugMode.toString(),
      'Release Mode': kReleaseMode.toString(),
      'Profile Mode': kProfileMode.toString(),
      'Is Web': kIsWeb.toString(),
    };

    for (final entry in info.entries) {
      Backstage.I.logger.i('${entry.key}: ${entry.value}', tag: 'system_info');
    }
  }
}
6
likes
150
points
351
downloads

Publisher

verified publisherdavidlevey.dev

Weekly Downloads

Complete in-app debugging console with advanced logging, network monitoring, performance metrics, and export capabilities. Production-ready with security features.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

archive, battery_plus, connectivity_plus, crypto, csv, dio, flutter, local_auth, path_provider, share_plus, shared_preferences, sqflite

More

Packages that depend on backstage