voo_logging 0.4.9 copy "voo_logging: ^0.4.9" to clipboard
voo_logging: ^0.4.9 copied to clipboard

Zero-config Flutter logging with pretty console output, persistent storage, DevTools integration, and Dio interceptor. Just call VooLogger.info() and it works.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:voo_logging/voo_logging.dart';
import 'package:dio/dio.dart';
import 'package:voo_toast/voo_toast.dart';

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

  // VooLogger now works with ZERO configuration!
  // Just call logging methods directly - it auto-initializes with smart defaults.
  //
  // For explicit control, use:
  // await VooLogger.ensureInitialized(config: LoggingConfig.production());

  // Optional: Initialize VooToast for toast notifications
  try {
    VooToastController.instance;
  } catch (_) {
    VooToastController.init();
  }

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'VooLogging Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo, brightness: Brightness.light),
        useMaterial3: true,
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo, brightness: Brightness.dark),
        useMaterial3: true,
      ),
      home: const VooToastOverlay(child: LoggingDemoScreen()),
    );
  }
}

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

  @override
  State<LoggingDemoScreen> createState() => _LoggingDemoScreenState();
}

class _LoggingDemoScreenState extends State<LoggingDemoScreen> {
  late final Dio dio;
  List<LogEntry> recentLogs = [];
  LogStatistics? stats;
  bool toastEnabled = false;
  int _selectedTab = 0;

  // Config options
  String _selectedPreset = 'default';
  bool _enablePrettyLogs = true;
  bool _showEmojis = true;
  bool _showTimestamp = true;
  bool _showBorders = true;
  bool _showMetadata = true;
  LogLevel _minimumLevel = LogLevel.verbose;

  @override
  void initState() {
    super.initState();
    _setupDio();
    _listenToLogs();
    _loadStats();
  }

  void _setupDio() {
    dio = Dio();
    final interceptor = VooDioInterceptor();
    dio.interceptors.add(InterceptorsWrapper(onRequest: interceptor.onRequest, onResponse: interceptor.onResponse, onError: interceptor.onError));
  }

  void _listenToLogs() {
    VooLogger.instance.stream.listen((log) {
      setState(() {
        recentLogs.insert(0, log);
        if (recentLogs.length > 50) recentLogs.removeLast();
      });
      _loadStats();
    });
  }

  Future<void> _loadStats() async {
    final newStats = await VooLogger.instance.getStatistics();
    setState(() => stats = newStats);
  }

  Future<void> _clearLogs() async {
    await VooLogger.instance.clearLogs();
    setState(() => recentLogs.clear());
    _loadStats();
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return Scaffold(
      appBar: AppBar(
        title: const Text('VooLogging Demo'),
        centerTitle: true,
        actions: [
          IconButton(
            icon: Icon(toastEnabled ? Icons.notifications_active : Icons.notifications_off_outlined),
            onPressed: () => setState(() => toastEnabled = !toastEnabled),
            tooltip: 'Toggle toast notifications',
          ),
          IconButton(icon: const Icon(Icons.delete_outline), onPressed: _clearLogs, tooltip: 'Clear logs'),
        ],
      ),
      body: Column(
        children: [
          // Tab bar
          Container(
            color: colorScheme.surfaceContainerHighest,
            child: Row(
              children: [
                _buildTab('Quick Log', 0, Icons.flash_on),
                _buildTab('Categories', 1, Icons.category),
                _buildTab('Network', 2, Icons.wifi),
                _buildTab('Config', 3, Icons.tune),
                _buildTab('Stats', 4, Icons.analytics),
              ],
            ),
          ),
          // Tab content
          Expanded(
            child: IndexedStack(index: _selectedTab, children: [_buildQuickLogTab(), _buildCategoriesTab(), _buildNetworkTab(), _buildConfigTab(), _buildStatsTab()]),
          ),
          // Log stream
          _buildLogStream(),
        ],
      ),
    );
  }

  Widget _buildTab(String label, int index, IconData icon) {
    final isSelected = _selectedTab == index;
    final colorScheme = Theme.of(context).colorScheme;

    return Expanded(
      child: InkWell(
        onTap: () => setState(() => _selectedTab = index),
        child: Container(
          padding: const EdgeInsets.symmetric(vertical: 12),
          decoration: BoxDecoration(
            border: Border(bottom: BorderSide(color: isSelected ? colorScheme.primary : Colors.transparent, width: 2)),
          ),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Icon(icon, size: 20, color: isSelected ? colorScheme.primary : colorScheme.onSurfaceVariant),
              const SizedBox(height: 4),
              Text(
                label,
                style: TextStyle(
                  fontSize: 12,
                  fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
                  color: isSelected ? colorScheme.primary : colorScheme.onSurfaceVariant,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildQuickLogTab() {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _buildSectionTitle('Log Levels'),
          const SizedBox(height: 8),
          Wrap(
            spacing: 8,
            runSpacing: 8,
            children: [
              _buildLogButton('Verbose', Colors.grey, () => VooLogger.verbose('Verbose trace message')),
              _buildLogButton('Debug', Colors.blueGrey, () => VooLogger.debug('Debug information')),
              _buildLogButton('Info', Colors.blue, () => VooLogger.info('Info message', shouldNotify: toastEnabled)),
              _buildLogButton('Warning', Colors.orange, () => VooLogger.warning('Warning alert', shouldNotify: toastEnabled)),
              _buildLogButton('Error', Colors.red, () => VooLogger.error('Error occurred', error: Exception('Sample error'), shouldNotify: toastEnabled)),
              _buildLogButton('Fatal', Colors.red.shade900, () => VooLogger.fatal('Fatal crash!', error: Exception('Critical failure'), shouldNotify: toastEnabled)),
            ],
          ),
          const SizedBox(height: 24),
          _buildSectionTitle('Quick Actions'),
          const SizedBox(height: 8),
          Wrap(
            spacing: 8,
            runSpacing: 8,
            children: [
              FilledButton.icon(onPressed: _logWithMetadata, icon: const Icon(Icons.data_object, size: 18), label: const Text('Log with Metadata')),
              FilledButton.tonalIcon(onPressed: _logMultiple, icon: const Icon(Icons.burst_mode, size: 18), label: const Text('Log 10 Messages')),
            ],
          ),
          const SizedBox(height: 24),
          _buildCodeExample('Zero-Config Usage', '''// Just use it - auto-initializes!
VooLogger.info('Hello world');

// With metadata
VooLogger.info('User action',
  category: 'Analytics',
  tag: 'button_click',
  metadata: {'screen': 'home'},
);'''),
        ],
      ),
    );
  }

  Widget _buildCategoriesTab() {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _buildSectionTitle('Log by Category'),
          const SizedBox(height: 8),
          Wrap(
            spacing: 8,
            runSpacing: 8,
            children: [
              _buildCategoryButton('Auth', Icons.lock, Colors.purple, () {
                VooLogger.info('User logged in', category: 'Auth', tag: 'login', metadata: {'method': 'email'});
              }),
              _buildCategoryButton('Network', Icons.cloud, Colors.blue, () {
                VooLogger.info('API request completed', category: 'Network', tag: 'api', metadata: {'endpoint': '/users'});
              }),
              _buildCategoryButton('Analytics', Icons.analytics, Colors.green, () {
                VooLogger.info('Event tracked', category: 'Analytics', tag: 'event', metadata: {'name': 'page_view'});
              }),
              _buildCategoryButton('Payment', Icons.payment, Colors.orange, () {
                VooLogger.info('Payment processed', category: 'Payment', tag: 'transaction', metadata: {'amount': 99.99});
              }),
              _buildCategoryButton('System', Icons.settings, Colors.grey, () {
                VooLogger.debug('System check', category: 'System', tag: 'health');
              }),
              _buildCategoryButton('Error', Icons.error, Colors.red, () {
                VooLogger.error('Operation failed', category: 'Error', tag: 'failure', error: Exception('Database error'));
              }),
            ],
          ),
          const SizedBox(height: 24),
          _buildCodeExample('Structured Logging', '''VooLogger.info(
  'User completed purchase',
  category: 'Payment',
  tag: 'checkout_complete',
  metadata: {
    'orderId': 'ORD-123',
    'amount': 99.99,
    'currency': 'USD',
    'items': 3,
  },
);'''),
        ],
      ),
    );
  }

  Widget _buildNetworkTab() {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _buildSectionTitle('Network Logging with Dio'),
          const SizedBox(height: 8),
          Wrap(
            spacing: 8,
            runSpacing: 8,
            children: [
              FilledButton.icon(
                onPressed: () => _makeRequest('https://jsonplaceholder.typicode.com/posts/1'),
                icon: const Icon(Icons.download, size: 18),
                label: const Text('GET Request'),
              ),
              FilledButton.icon(
                onPressed: () => _makeRequest('https://jsonplaceholder.typicode.com/posts', isPost: true),
                icon: const Icon(Icons.upload, size: 18),
                label: const Text('POST Request'),
              ),
              FilledButton.tonalIcon(onPressed: () => _makeRequest('https://httpstat.us/404'), icon: const Icon(Icons.error_outline, size: 18), label: const Text('404 Error')),
              FilledButton.tonalIcon(onPressed: () => _makeRequest('https://httpstat.us/500'), icon: const Icon(Icons.dangerous, size: 18), label: const Text('500 Error')),
            ],
          ),
          const SizedBox(height: 24),
          _buildCodeExample('Dio Integration', '''final dio = Dio();
final interceptor = VooDioInterceptor();

dio.interceptors.add(InterceptorsWrapper(
  onRequest: interceptor.onRequest,
  onResponse: interceptor.onResponse,
  onError: interceptor.onError,
));

// All requests are now logged automatically!
await dio.get('https://api.example.com/data');'''),
        ],
      ),
    );
  }

  Widget _buildConfigTab() {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _buildSectionTitle('Configuration Presets'),
          const SizedBox(height: 8),
          Wrap(
            spacing: 8,
            runSpacing: 8,
            children: [
              _buildPresetButton('Default', 'default', Colors.blue),
              _buildPresetButton('Development', 'development', Colors.green),
              _buildPresetButton('Production', 'production', Colors.orange),
              _buildPresetButton('Minimal', 'minimal', Colors.grey),
            ],
          ),
          const SizedBox(height: 24),
          _buildSectionTitle('Format Options'),
          const SizedBox(height: 8),
          _buildToggleOption('Pretty Logs', _enablePrettyLogs, (v) {
            setState(() => _enablePrettyLogs = v);
            _applyConfig();
          }),
          _buildToggleOption('Show Emojis', _showEmojis, (v) {
            setState(() => _showEmojis = v);
            _applyConfig();
          }),
          _buildToggleOption('Show Timestamp', _showTimestamp, (v) {
            setState(() => _showTimestamp = v);
            _applyConfig();
          }),
          _buildToggleOption('Show Borders', _showBorders, (v) {
            setState(() => _showBorders = v);
            _applyConfig();
          }),
          _buildToggleOption('Show Metadata', _showMetadata, (v) {
            setState(() => _showMetadata = v);
            _applyConfig();
          }),
          const SizedBox(height: 16),
          _buildSectionTitle('Minimum Log Level'),
          const SizedBox(height: 8),
          Wrap(
            spacing: 8,
            runSpacing: 8,
            children: LogLevel.values.map((level) {
              final isSelected = _minimumLevel == level;
              return FilterChip(
                label: Text(level.name),
                selected: isSelected,
                onSelected: (_) {
                  setState(() => _minimumLevel = level);
                  _applyConfig();
                },
                selectedColor: _getLevelColor(level.name).withAlpha(50),
                checkmarkColor: _getLevelColor(level.name),
              );
            }).toList(),
          ),
          const SizedBox(height: 24),
          _buildSectionTitle('Test Current Config'),
          const SizedBox(height: 8),
          FilledButton.icon(onPressed: _logAllLevels, icon: const Icon(Icons.play_arrow), label: const Text('Log All Levels')),
          const SizedBox(height: 24),
          _buildCodeExample('Current Config Code', _getConfigCode()),
        ],
      ),
    );
  }

  Widget _buildPresetButton(String label, String preset, Color color) {
    final isSelected = _selectedPreset == preset;
    return FilterChip(
      label: Text(label),
      selected: isSelected,
      onSelected: (_) => _applyPreset(preset),
      selectedColor: color.withAlpha(50),
      checkmarkColor: color,
      avatar: isSelected ? Icon(Icons.check, size: 18, color: color) : null,
    );
  }

  Widget _buildToggleOption(String label, bool value, ValueChanged<bool> onChanged) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(label),
          Switch(value: value, onChanged: onChanged),
        ],
      ),
    );
  }

  void _applyPreset(String preset) {
    setState(() {
      _selectedPreset = preset;
      switch (preset) {
        case 'development':
          _enablePrettyLogs = true;
          _showEmojis = true;
          _showTimestamp = true;
          _showBorders = true;
          _showMetadata = true;
          _minimumLevel = LogLevel.verbose;
        case 'production':
          _enablePrettyLogs = false;
          _showEmojis = false;
          _showTimestamp = true;
          _showBorders = false;
          _showMetadata = false;
          _minimumLevel = LogLevel.warning;
        case 'minimal':
          _enablePrettyLogs = false;
          _showEmojis = false;
          _showTimestamp = false;
          _showBorders = false;
          _showMetadata = false;
          _minimumLevel = LogLevel.info;
        default:
          _enablePrettyLogs = true;
          _showEmojis = true;
          _showTimestamp = true;
          _showBorders = true;
          _showMetadata = true;
          _minimumLevel = LogLevel.verbose;
      }
    });
    _applyConfig();
  }

  Future<void> _applyConfig() async {
    final config = LoggingConfig(
      enablePrettyLogs: _enablePrettyLogs,
      showEmojis: _showEmojis,
      showTimestamp: _showTimestamp,
      showBorders: _showBorders,
      showMetadata: _showMetadata,
      minimumLevel: _minimumLevel,
    );
    await VooLogger.initialize(config: config);
  }

  void _logAllLevels() {
    VooLogger.verbose('This is a verbose message - detailed tracing info');
    VooLogger.debug('This is a debug message - debugging information');
    VooLogger.info('This is an info message - general information');
    VooLogger.warning('This is a warning message - something to watch');
    VooLogger.error('This is an error message', error: Exception('Sample error'));
    VooLogger.fatal('This is a fatal message', error: Exception('Critical failure'));
  }

  String _getConfigCode() {
    final buffer = StringBuffer();
    buffer.writeln('LoggingConfig(');
    buffer.writeln('  enablePrettyLogs: $_enablePrettyLogs,');
    buffer.writeln('  showEmojis: $_showEmojis,');
    buffer.writeln('  showTimestamp: $_showTimestamp,');
    buffer.writeln('  showBorders: $_showBorders,');
    buffer.writeln('  showMetadata: $_showMetadata,');
    buffer.writeln('  minimumLevel: LogLevel.${_minimumLevel.name},');
    buffer.writeln(')');
    return buffer.toString();
  }

  Widget _buildStatsTab() {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _buildSectionTitle('Log Statistics'),
          const SizedBox(height: 8),
          if (stats != null) ...[
            _buildStatCard('Total Logs', stats!.totalLogs.toString(), Icons.list_alt, Colors.blue),
            const SizedBox(height: 12),
            _buildSectionTitle('By Level'),
            const SizedBox(height: 8),
            Wrap(
              spacing: 8,
              runSpacing: 8,
              children: stats!.levelCounts.entries.map((e) {
                return _buildStatChip(e.key, e.value, _getLevelColor(e.key));
              }).toList(),
            ),
            if (stats!.categoryCounts.isNotEmpty) ...[
              const SizedBox(height: 16),
              _buildSectionTitle('By Category'),
              const SizedBox(height: 8),
              Wrap(
                spacing: 8,
                runSpacing: 8,
                children: stats!.categoryCounts.entries.map((e) {
                  return _buildStatChip(e.key, e.value, Colors.indigo);
                }).toList(),
              ),
            ],
          ] else
            const Center(child: CircularProgressIndicator()),
          const SizedBox(height: 24),
          _buildSectionTitle('Configuration'),
          const SizedBox(height: 8),
          _buildConfigInfo(),
        ],
      ),
    );
  }

  Widget _buildLogStream() {
    final colorScheme = Theme.of(context).colorScheme;

    return Container(
      height: 200,
      decoration: BoxDecoration(
        color: colorScheme.surfaceContainerLowest,
        border: Border(top: BorderSide(color: colorScheme.outlineVariant)),
      ),
      child: Column(
        children: [
          Container(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            color: colorScheme.surfaceContainerHighest,
            child: Row(
              children: [
                Icon(Icons.terminal, size: 16, color: colorScheme.onSurfaceVariant),
                const SizedBox(width: 8),
                Text(
                  'Log Stream',
                  style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: colorScheme.onSurfaceVariant),
                ),
                const Spacer(),
                Text('${recentLogs.length} logs', style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant)),
              ],
            ),
          ),
          Expanded(
            child: recentLogs.isEmpty
                ? Center(
                    child: Text('No logs yet', style: TextStyle(color: colorScheme.onSurfaceVariant)),
                  )
                : ListView.builder(
                    padding: const EdgeInsets.symmetric(horizontal: 8),
                    itemCount: recentLogs.length,
                    itemBuilder: (context, index) {
                      final log = recentLogs[index];
                      return _buildLogEntry(log);
                    },
                  ),
          ),
        ],
      ),
    );
  }

  Widget _buildLogEntry(LogEntry log) {
    final color = _getLevelColor(log.level.name);
    final time = '${log.timestamp.hour.toString().padLeft(2, '0')}:${log.timestamp.minute.toString().padLeft(2, '0')}:${log.timestamp.second.toString().padLeft(2, '0')}';

    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 2),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            time,
            style: TextStyle(fontSize: 11, fontFamily: 'monospace', color: Colors.grey.shade600),
          ),
          const SizedBox(width: 8),
          Container(
            padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
            decoration: BoxDecoration(color: color.withAlpha(30), borderRadius: BorderRadius.circular(4)),
            child: Text(
              log.level.name.toUpperCase().substring(0, 3),
              style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold, color: color),
            ),
          ),
          const SizedBox(width: 8),
          if (log.category != null) ...[Text('[${log.category}]', style: TextStyle(fontSize: 11, color: Colors.grey.shade600)), const SizedBox(width: 4)],
          Expanded(
            child: Text(log.message, style: const TextStyle(fontSize: 12), overflow: TextOverflow.ellipsis),
          ),
        ],
      ),
    );
  }

  Widget _buildSectionTitle(String title) {
    return Text(title, style: Theme.of(context).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600));
  }

  Widget _buildLogButton(String label, Color color, VoidCallback onPressed) {
    return FilledButton(
      onPressed: onPressed,
      style: FilledButton.styleFrom(backgroundColor: color),
      child: Text(label),
    );
  }

  Widget _buildCategoryButton(String label, IconData icon, Color color, VoidCallback onPressed) {
    return FilledButton.icon(
      onPressed: onPressed,
      style: FilledButton.styleFrom(backgroundColor: color),
      icon: Icon(icon, size: 18),
      label: Text(label),
    );
  }

  Widget _buildStatCard(String title, String value, IconData icon, Color color) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          children: [
            Icon(icon, size: 32, color: color),
            const SizedBox(width: 16),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(title, style: Theme.of(context).textTheme.bodySmall),
                Text(value, style: Theme.of(context).textTheme.headlineMedium?.copyWith(fontWeight: FontWeight.bold)),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildStatChip(String label, int count, Color color) {
    return Chip(
      avatar: CircleAvatar(
        backgroundColor: color,
        radius: 10,
        child: Text('$count', style: const TextStyle(fontSize: 10, color: Colors.white)),
      ),
      label: Text(label),
    );
  }

  Widget _buildConfigInfo() {
    final config = VooLogger.config;
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _buildConfigRow('Min Level', config.minimumLevel.name),
            _buildConfigRow('Pretty Logs', config.enablePrettyLogs.toString()),
            _buildConfigRow('Max Logs', config.maxLogs?.toString() ?? 'Unlimited'),
            _buildConfigRow('Retention Days', config.retentionDays?.toString() ?? 'Forever'),
            _buildConfigRow('Auto Cleanup', config.autoCleanup.toString()),
          ],
        ),
      ),
    );
  }

  Widget _buildConfigRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(label, style: const TextStyle(fontWeight: FontWeight.w500)),
          Text(value, style: TextStyle(color: Theme.of(context).colorScheme.primary)),
        ],
      ),
    );
  }

  Widget _buildCodeExample(String title, String code) {
    final colorScheme = Theme.of(context).colorScheme;

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        _buildSectionTitle(title),
        const SizedBox(height: 8),
        Container(
          width: double.infinity,
          padding: const EdgeInsets.all(12),
          decoration: BoxDecoration(color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(8)),
          child: Text(
            code,
            style: TextStyle(fontFamily: 'monospace', fontSize: 12, color: colorScheme.onSurfaceVariant),
          ),
        ),
      ],
    );
  }

  Color _getLevelColor(String level) {
    switch (level.toLowerCase()) {
      case 'verbose':
        return Colors.grey;
      case 'debug':
        return Colors.blueGrey;
      case 'info':
        return Colors.blue;
      case 'warning':
        return Colors.orange;
      case 'error':
        return Colors.red;
      case 'fatal':
        return Colors.red.shade900;
      default:
        return Colors.grey;
    }
  }

  void _logWithMetadata() {
    VooLogger.info(
      'User interaction logged',
      category: 'Analytics',
      tag: 'user_action',
      metadata: {'screen': 'demo', 'action': 'button_click', 'timestamp': DateTime.now().toIso8601String(), 'sessionId': 'demo-session-123'},
      shouldNotify: toastEnabled,
    );
  }

  Future<void> _logMultiple() async {
    for (var i = 1; i <= 10; i++) {
      await VooLogger.info('Batch log #$i', category: 'Batch', tag: 'test');
      await Future.delayed(const Duration(milliseconds: 50));
    }
  }

  Future<void> _makeRequest(String url, {bool isPost = false}) async {
    try {
      if (isPost) {
        await dio.post(url, data: {'title': 'Test', 'body': 'Content'});
      } else {
        await dio.get(url);
      }
      VooLogger.info('Request completed: $url', category: 'Network', shouldNotify: toastEnabled);
    } catch (e) {
      VooLogger.error('Request failed: $url', category: 'Network', error: e, shouldNotify: toastEnabled);
    }
  }
}
6
likes
150
points
590
downloads

Publisher

verified publishervoostack.com

Weekly Downloads

Zero-config Flutter logging with pretty console output, persistent storage, DevTools integration, and Dio interceptor. Just call VooLogger.info() and it works.

Homepage
Repository (GitHub)
View/report issues

Topics

#logging #debugging #devtools #dio

Documentation

API reference

License

MIT (license)

Dependencies

equatable, flutter, json_annotation, path, path_provider, sembast, sembast_web, voo_core, voo_toast

More

Packages that depend on voo_logging