in_app_console 2.0.1  in_app_console: ^2.0.1 copied to clipboard
in_app_console: ^2.0.1 copied to clipboard
In-app console for real-time log viewing. Bridges developers and testers with unified logging across micro-frontend modules. Extensible with custom plugins.
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:in_app_console/in_app_console.dart';
import 'extensions/log_statistics_extension.dart';
void main() {
  /// Enable the in app console for debugging purposes.
  InAppConsole.kEnableConsole = true;
  // Initialize micro-frontend modules
  MicroFrontendApp.initialize();
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) => MaterialApp(
        title: 'Micro-Frontend Console Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          useMaterial3: true,
        ),
        home: const HomeScreen(),
      );
}
/// Central application that manages all micro-frontend modules
class MicroFrontendApp {
  static late AuthModule authModule;
  static late PaymentModule paymentModule;
  static late ProfileModule profileModule;
  static late ChatModule chatModule;
  static void initialize() {
    // Initialize all modules
    authModule = AuthModule();
    paymentModule = PaymentModule();
    profileModule = ProfileModule();
    chatModule = ChatModule();
    // Register all module loggers with the central console
    InAppConsole.instance.addLogger(authModule.logger);
    InAppConsole.instance.addLogger(paymentModule.logger);
    InAppConsole.instance.addLogger(profileModule.logger);
    InAppConsole.instance.addLogger(chatModule.logger);
    // Register extensions
    InAppConsole.instance.registerExtension(LogStatisticsExtension());
  }
}
/// Authentication Module - Handles user login/logout
class AuthModule {
  final InAppLogger logger = InAppLogger()..setLabel('Auth');
  bool _isLoggedIn = false;
  String? _currentUser;
  bool get isLoggedIn => _isLoggedIn;
  String? get currentUser => _currentUser;
  Future<bool> login(String username, String password) async {
    logger.logInfo('Login attempt for user: $username');
    // Simulate network delay
    await Future.delayed(const Duration(milliseconds: 800));
    if (username.isNotEmpty && password.length >= 6) {
      _isLoggedIn = true;
      _currentUser = username;
      logger.logInfo('Login successful for user: $username');
      return true;
    } else {
      logger.logError(
        message: 'Login failed for user: $username - Invalid credentials',
        error: ArgumentError('Invalid username or password'),
        stackTrace: StackTrace.current,
      );
      return false;
    }
  }
  void logout() {
    logger.logInfo('User logout: $_currentUser');
    _isLoggedIn = false;
    _currentUser = null;
  }
  void validateSession() {
    if (_isLoggedIn) {
      logger.logInfo('Session validation successful for: $_currentUser');
    } else {
      logger.logWarning(message: 'No active session found');
    }
  }
}
/// Payment Module - Handles payment processing
class PaymentModule {
  final InAppLogger logger = InAppLogger()..setLabel('Payment');
  Future<bool> processPayment(double amount, String method) async {
    logger.logInfo(
        'Processing payment: \$${amount.toStringAsFixed(2)} via $method');
    // Simulate payment processing
    await Future.delayed(const Duration(milliseconds: 1200));
    // Simulate random payment failures
    if (Random().nextBool()) {
      logger.logError(
        message: 'Payment failed: Gateway timeout',
        error: StateError('Payment gateway not responding'),
        stackTrace: StackTrace.current,
      );
      return false;
    }
    // Simulate slow payment warnings
    if (Random().nextBool()) {
      logger.logWarning(
          message:
              'Payment processing slower than usual (${Random().nextInt(3) + 2}s)');
    }
    logger.logInfo('Payment successful: \$${amount.toStringAsFixed(2)}');
    return true;
  }
  void validatePaymentMethod(String method) {
    logger.logInfo('Validating payment method: $method');
    if (method.isEmpty) {
      logger.logError(
        message: 'Invalid payment method',
        error: ArgumentError('Payment method cannot be empty'),
        stackTrace: StackTrace.current,
      );
    }
  }
}
/// Profile Module - Handles user profile management
class ProfileModule {
  final InAppLogger logger = InAppLogger()..setLabel('Profile');
  Map<String, dynamic> _profileData = {};
  Future<void> updateProfile(Map<String, dynamic> data) async {
    logger.logInfo('Updating profile data: ${data.keys.join(', ')}');
    await Future.delayed(const Duration(milliseconds: 600));
    _profileData.addAll(data);
    logger.logInfo('Profile updated successfully');
  }
  Future<void> uploadProfileImage() async {
    logger.logInfo('Starting profile image upload');
    // Simulate upload progress
    for (int i = 1; i <= 5; i++) {
      await Future.delayed(const Duration(milliseconds: 200));
      logger.logInfo('Upload progress: ${i * 20}%');
    }
    logger.logInfo('Profile image uploaded successfully');
  }
  void fetchProfile(String userId) {
    logger.logInfo('Fetching profile for user: $userId');
    if (userId.isEmpty) {
      logger.logError(
        message: 'Cannot fetch profile: Invalid user ID',
        error: ArgumentError('User ID is required'),
        stackTrace: StackTrace.current,
      );
    }
  }
}
/// Chat Module - Handles messaging functionality
class ChatModule {
  final InAppLogger logger = InAppLogger()..setLabel('Chat');
  final List<String> _messages = [];
  Future<void> sendMessage(String message, String recipient) async {
    logger.logInfo('Sending message to $recipient');
    await Future.delayed(const Duration(milliseconds: 400));
    if (message.trim().isEmpty) {
      logger.logError(
        message: 'Cannot send empty message',
        error: ArgumentError('Message content is required'),
        stackTrace: StackTrace.current,
      );
      return;
    }
    _messages.add(message);
    logger.logInfo('Message sent successfully to $recipient');
  }
  void connectToChat() {
    logger.logInfo('Connecting to chat server');
    // Simulate connection issues occasionally
    if (Random().nextInt(10) < 2) {
      logger.logWarning(message: 'Chat connection unstable - retrying...');
    } else {
      logger.logInfo('Connected to chat server successfully');
    }
  }
  void receiveMessage(String from, String message) {
    logger.logInfo('Received message from $from');
    _messages.add('$from: $message');
  }
}
class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});
  @override
  State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  bool _isLoading = false;
  @override
  void initState() {
    super.initState();
    // Simulate app startup logs
    _simulateAppStartup();
  }
  void _simulateAppStartup() {
    Timer(const Duration(milliseconds: 500), () {
      MicroFrontendApp.authModule.validateSession();
      MicroFrontendApp.chatModule.connectToChat();
    });
  }
  Future<void> _simulateUserJourney() async {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (mounted) {
        setState(() => _isLoading = true);
      }
    });
    try {
      // 1. Login
      await MicroFrontendApp.authModule.login('john_doe', 'password123');
      // 2. Fetch profile
      MicroFrontendApp.profileModule.fetchProfile('john_doe');
      // 3. Update profile
      await MicroFrontendApp.profileModule
          .updateProfile({'name': 'John Doe', 'email': 'john@example.com'});
      // 4. Process payment
      await MicroFrontendApp.paymentModule.processPayment(29.99, 'Credit Card');
      // 5. Send chat message
      await MicroFrontendApp.chatModule.sendMessage('Hello there!', 'support');
      // 6. Upload profile image
      await MicroFrontendApp.profileModule.uploadProfileImage();
    } finally {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted) {
          setState(() => _isLoading = false);
        }
      });
    }
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Micro-Frontend Console Demo'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        actions: [
          IconButton(
            icon: const Icon(Icons.bug_report),
            onPressed: () => InAppConsole.instance.openConsole(context),
            tooltip: 'Open Console',
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Header
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  children: [
                    const Icon(Icons.architecture,
                        size: 48, color: Colors.blue),
                    const SizedBox(height: 8),
                    const Text(
                      'Micro-Frontend Architecture Demo',
                      style:
                          TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 8),
                    const Text(
                      'This demo shows how multiple modules (Auth, Payment, Profile, Chat) log to a central console',
                      textAlign: TextAlign.center,
                      style: TextStyle(color: Colors.grey),
                    ),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 24),
            // Quick Demo Button
            ElevatedButton.icon(
              onPressed: _isLoading ? null : _simulateUserJourney,
              icon: _isLoading
                  ? const SizedBox(
                      width: 16,
                      height: 16,
                      child: CircularProgressIndicator(strokeWidth: 2))
                  : const Icon(Icons.play_arrow),
              label: Text(
                  _isLoading ? 'Running Demo...' : 'Run Complete User Journey'),
              style: ElevatedButton.styleFrom(
                padding: const EdgeInsets.all(16),
                backgroundColor: Colors.green,
                foregroundColor: Colors.white,
              ),
            ),
            const SizedBox(height: 24),
            // Module Actions
            const Text(
              'Individual Module Actions:',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 16),
            // Auth Module
            _buildModuleCard(
              'Authentication Module',
              Icons.security,
              Colors.blue,
              [
                ElevatedButton(
                  onPressed: () =>
                      MicroFrontendApp.authModule.login('testuser', 'pass123'),
                  child: const Text('Simulate Login'),
                ),
                ElevatedButton(
                  onPressed: () => MicroFrontendApp.authModule.logout(),
                  child: const Text('Simulate Logout'),
                ),
              ],
            ),
            // Payment Module
            _buildModuleCard(
              'Payment Module',
              Icons.payment,
              Colors.green,
              [
                ElevatedButton(
                  onPressed: () => MicroFrontendApp.paymentModule
                      .processPayment(
                          Random().nextDouble() * 100 + 10,
                          [
                            'Credit Card',
                            'PayPal',
                            'Apple Pay'
                          ][Random().nextInt(3)]),
                  child: const Text('Process Payment'),
                ),
                ElevatedButton(
                  onPressed: () => MicroFrontendApp.paymentModule
                      .validatePaymentMethod('Credit Card'),
                  child: const Text('Validate Payment'),
                ),
              ],
            ),
            // Profile Module
            _buildModuleCard(
              'Profile Module',
              Icons.person,
              Colors.orange,
              [
                ElevatedButton(
                  onPressed: () =>
                      MicroFrontendApp.profileModule.updateProfile({
                    'name': 'User ${Random().nextInt(100)}',
                    'age': Random().nextInt(50) + 18,
                  }),
                  child: const Text('Update Profile'),
                ),
                ElevatedButton(
                  onPressed: () =>
                      MicroFrontendApp.profileModule.uploadProfileImage(),
                  child: const Text('Upload Image'),
                ),
              ],
            ),
            // Chat Module
            _buildModuleCard(
              'Chat Module',
              Icons.chat,
              Colors.purple,
              [
                ElevatedButton(
                  onPressed: () => MicroFrontendApp.chatModule.sendMessage(
                      'Hello from user ${Random().nextInt(100)}!', 'support'),
                  child: const Text('Send Message'),
                ),
                ElevatedButton(
                  onPressed: () => MicroFrontendApp.chatModule.connectToChat(),
                  child: const Text('Reconnect Chat'),
                ),
              ],
            ),
            const SizedBox(height: 24),
            // Console Button
            Card(
              color: Colors.red.shade50,
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  children: [
                    const Icon(Icons.bug_report, size: 32, color: Colors.red),
                    const SizedBox(height: 8),
                    const Text(
                      'View Unified Console',
                      style:
                          TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 8),
                    const Text(
                      'See logs from all modules in one place',
                      style: TextStyle(color: Colors.grey),
                    ),
                    const SizedBox(height: 12),
                    ElevatedButton.icon(
                      onPressed: () =>
                          InAppConsole.instance.openConsole(context),
                      icon: const Icon(Icons.visibility),
                      label: const Text('Open In-App Console'),
                      style: ElevatedButton.styleFrom(
                        backgroundColor: Colors.red,
                        foregroundColor: Colors.white,
                        padding: const EdgeInsets.symmetric(
                            horizontal: 24, vertical: 12),
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
  Widget _buildModuleCard(
      String title, IconData icon, Color color, List<Widget> actions) {
    return Card(
      margin: const EdgeInsets.only(bottom: 16),
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(icon, color: color, size: 24),
                const SizedBox(width: 8),
                Text(
                  title,
                  style: const TextStyle(
                      fontSize: 16, fontWeight: FontWeight.bold),
                ),
              ],
            ),
            const SizedBox(height: 12),
            Wrap(
              spacing: 8,
              runSpacing: 8,
              children: actions,
            ),
          ],
        ),
      ),
    );
  }
  @override
  void dispose() {
    _usernameController.dispose();
    _passwordController.dispose();
    super.dispose();
  }
}