inline_logger 0.3.1 copy "inline_logger: ^0.3.1" to clipboard
inline_logger: ^0.3.1 copied to clipboard

A powerful inline logger for Flutter. Log anywhere in your widget tree without breakpoints. Chain logging calls directly on any expression.

example/lib/main.dart

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

void main() {
  // This example runs on the 0.3.0 defaults so the console shows what a
  // new user actually gets: the log's key in the IDE's own gutter, a
  // fixed-width level token, and a dimmed clickable location.
  LoggerConfig.minLevel = LogLevel.debug;
  LoggerConfig.useColors = true; // Enable colored console output

  // Both default to false in 0.3.0. Uncomment for a plain `flutter run`
  // terminal, which has no time column of its own.
  // LoggerConfig.showTimestamp = true;
  // LoggerConfig.showEmoji = true;

  // NEW: Source location is on by default in debug mode.
  // These are the defaults, shown here for demonstration:
  LoggerConfig.showSourceLocation = true;
  LoggerConfig.clickableLinkFormat = LinkFormat.auto;
  LoggerConfig.useClickableLinks = true;

  // Optional: Hook into every log record
  LoggerConfig.onRecord = (record) {
    // You could forward to Crashlytics, Sentry, etc.
    // print('Record: ${record.level.label} - ${record.message}');
    // print('Source: ${record.source}');
  };

  // Direct call — the clickable link will point to THIS line:
  Logger.info('App starting', 'main');

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'inline_logger Example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const ExampleHomePage(),
    );
  }
}

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

  @override
  State<ExampleHomePage> createState() => _ExampleHomePageState();
}

class _ExampleHomePageState extends State<ExampleHomePage> {
  int _counter = 0;
  List<String> _items = [];
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    Logger.lifecycle('initState', 'ExampleHomePage initialized');
  }

  void _incrementCounter() {
    Logger.header('INCREMENT COUNTER');

    setState(() {
      // Inline call — the clickable link will point to THIS line:
      _counter = (_counter + 1).logSuccess('New counter value');
    });

    Logger.divider();
  }

  Future<void> _simulateApiCall() async {
    Logger.header('SIMULATE API CALL');

    setState(() {
      _isLoading = true;
      Logger.state('isLoading', _isLoading);
    });

    Logger.apiRequest(
      endpoint: '/api/items',
      method: 'GET',
    );

    // Simulate network delay
    final stopwatch = Stopwatch()..start();
    await Future.delayed(const Duration(seconds: 2));
    stopwatch.stop();

    final mockData = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'];

    Logger.apiResponse(
      endpoint: '/api/items',
      statusCode: 200,
      data: mockData,
      duration: stopwatch.elapsed,
    );

    setState(() {
      _items = mockData.logSuccess('Items loaded');
      _isLoading = false;
      Logger.state('isLoading', _isLoading);
    });

    Logger.divider();
  }

  void _demonstrateLogLevels() {
    Logger.header('LOG LEVELS DEMO');

    Logger.debug('This is a debug message');
    Logger.verbose('This is a verbose message');
    Logger.info('This is an info message');
    Logger.success('This is a success message');
    Logger.warning('This is a warning message');
    Logger.error('This is an error message');
    Logger.critical('This is a critical message');

    Logger.divider();
  }

  void _demonstrateInlineLogging() {
    Logger.header('INLINE LOGGING DEMO');

    // Example 1: Simple inline logging
    'John Doe'.log('User name');

    // Example 2: Chained logging
    [1, 2, 3, 4, 5]
        .log('Initial list')
        .map((e) => e * 2)
        .toList()
        .logInfo('Doubled list')
        .where((e) => e > 5)
        .toList()
        .logSuccess('Filtered list');

    // Example 3: Different log levels
    const age = 25;
    age.logDebug('User age');
    age.logVerbose('User age verbose');
    age.logInfo('User age info');
    age.logSuccess('User age success');
    age.logWarning('User age warning');

    Logger.divider();

    // Show snackbar to confirm
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Check console for inline logging examples!'),
          duration: Duration(seconds: 2),
        ),
      );
    }
  }

  void _demonstrateSourceLocations() {
    Logger.header('SOURCE LOCATION DEMO');

    // Each of these shows a clickable link to THIS file.
    // The shipped default: the key becomes the console's own gutter.
    Logger.info('Direct call from _demonstrateSourceLocations', 'Demo');
    'inline value'.logDebug('Inline call');

    // Note: every LinkFormat except bareAbsolute renders identically for
    // files under lib/, because those frames are already package: URIs.
    LoggerConfig.clickableLinkFormat = LinkFormat.bareAbsolute;
    Logger.info('bareAbsolute — the only format that differs here', 'Demo');
    LoggerConfig.clickableLinkFormat = LinkFormat.auto;

    // The location on its own row: no `.dart` in a message can steal
    // the click, and the row can never blow past Dart-Code's parse cap.
    LoggerConfig.locationPlacement = LocationPlacement.ownLine;
    Logger.info('location on its own line', 'Demo');
    LoggerConfig.locationPlacement = LocationPlacement.inline;

    // Emoji level token — the glyph replaces DBG/VRB/INF/… entirely.
    LoggerConfig.levelStyle = LevelStyle.emoji;
    Logger.info('emoji level token', 'Demo');
    Logger.warning('emoji level token', 'Demo');
    Logger.error('emoji level token', 'Demo');
    LoggerConfig.levelStyle = LevelStyle.short;

    // The 0.2.x layout, for comparison.
    LoggerConfig.keyPlacement = KeyPlacement.inline;
    LoggerConfig.developerLogName = 'InlineLogger';
    LoggerConfig.showTimestamp = true;
    LoggerConfig.timestampStyle = TimestampStyle.iso;
    LoggerConfig.levelStyle = LevelStyle.full;
    LoggerConfig.showEmoji = true;
    Logger.info('the 0.2.x layout', 'Demo');

    // Location segment omitted entirely.
    LoggerConfig.useClickableLinks = false;
    Logger.info('no location segment', 'Demo');

    // Restore field by field rather than calling LoggerConfig.reset(),
    // which would also null the onRecord hook installed in main() and
    // clear the history the "Log History" button displays.
    LoggerConfig.keyPlacement = KeyPlacement.developerLogName;
    LoggerConfig.developerLogName = 'IL';
    LoggerConfig.showTimestamp = false;
    LoggerConfig.timestampStyle = TimestampStyle.clock;
    LoggerConfig.levelStyle = LevelStyle.short;
    LoggerConfig.showEmoji = false;
    LoggerConfig.useClickableLinks = true;
    Logger.divider();

    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text(
            'Check console for source location examples!',
          ),
          duration: Duration(seconds: 2),
        ),
      );
    }
  }

  void _showLogHistory() {
    Logger.header('LOG HISTORY');

    final history = LoggerConfig.logHistory;
    Logger.info('Total logs in history: ${history.length}');

    // Rendered through ConsoleFormatter.formatPlain: one ANSI-free,
    // single-line entry per record, ready for a file sink.
    for (final line in LoggerConfig.logHistoryStrings) {
      Logger.verbose(line);
    }

    Logger.divider();

    if (mounted) {
      showDialog(
        context: context,
        builder: (context) => AlertDialog(
          title: const Text('Log History'),
          content: Text(
            history.isEmpty
                ? 'No logs in history yet.\n\n'
                    'Try triggering errors or warnings!'
                : 'Found ${history.length} logs.\n\n'
                    'Each log includes source location.\n\n'
                    'Check console for details.',
          ),
          actions: [
            TextButton(
              onPressed: () {
                LoggerConfig.clearHistory();
                Logger.success('History cleared');
                Navigator.pop(context);
              },
              child: const Text('Clear History'),
            ),
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('Close'),
            ),
          ],
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('inline_logger Example').log('AppBar title'),
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Counter Section
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  children: [
                    const Text(
                      'Counter Example',
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 8),
                    Text(
                      'Count: ${_counter.log('Current counter')}',
                      style: Theme.of(context).textTheme.headlineMedium,
                    ),
                    const SizedBox(height: 8),
                    ElevatedButton.icon(
                      onPressed: _incrementCounter,
                      icon: const Icon(Icons.add),
                      label: const Text('Increment'),
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // API Call Section
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  children: [
                    const Text(
                      'API Call Example',
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 8),
                    if (_isLoading)
                      const CircularProgressIndicator()
                    else if (_items.isEmpty)
                      const Text('No items loaded yet')
                    else
                      Column(
                        children: _items
                            .map((item) => ListTile(
                                  leading: const Icon(Icons.check_circle),
                                  title: Text(item.log('List item')),
                                ))
                            .toList(),
                      ),
                    const SizedBox(height: 8),
                    ElevatedButton.icon(
                      onPressed: _isLoading ? null : _simulateApiCall,
                      icon: const Icon(Icons.cloud_download),
                      label: const Text('Fetch Items'),
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // Logging Examples
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: [
                    const Text(
                      'Logging Examples',
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 12),
                    ElevatedButton.icon(
                      onPressed: _demonstrateLogLevels,
                      icon: const Icon(Icons.format_list_bulleted),
                      label: const Text('Show All Log Levels'),
                    ),
                    const SizedBox(height: 8),
                    ElevatedButton.icon(
                      onPressed: _demonstrateInlineLogging,
                      icon: const Icon(Icons.link),
                      label: const Text('Inline Logging Demo'),
                    ),
                    const SizedBox(height: 8),
                    ElevatedButton.icon(
                      onPressed: _demonstrateSourceLocations,
                      icon: const Icon(Icons.location_on),
                      label: const Text('Source Location Demo'),
                    ),
                    const SizedBox(height: 8),
                    ElevatedButton.icon(
                      onPressed: _showLogHistory,
                      icon: const Icon(Icons.history),
                      label: const Text('View Log History'),
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // Info Card
            Card(
              color: Colors.blue.shade50,
              child: const Padding(
                padding: EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        Icon(Icons.info_outline, color: Colors.blue),
                        SizedBox(width: 8),
                        Expanded(
                          child: Text(
                            'Open your console to see logs with '
                            'clickable source locations!',
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              color: Colors.blue,
                            ),
                          ),
                        ),
                      ],
                    ),
                    SizedBox(height: 8),
                    Text(
                      'All logging is visible in your IDE console '
                      'or terminal. Click on the file paths in the '
                      'log output to jump directly to the source code. '
                      'Try clicking the buttons above to see different '
                      'logging features in action.',
                      style: TextStyle(fontSize: 12),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    Logger.lifecycle('dispose', 'ExampleHomePage disposed');
    super.dispose();
  }
}
9
likes
160
points
139
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A powerful inline logger for Flutter. Log anywhere in your widget tree without breakpoints. Chain logging calls directly on any expression.

Repository (GitHub)
View/report issues

Topics

#logging #debug #development #tools

License

MIT (license)

Dependencies

flutter

More

Packages that depend on inline_logger