telling_logger 1.4.3 copy "telling_logger: ^1.4.3" to clipboard
telling_logger: ^1.4.3 copied to clipboard

Comprehensive crash reporting, error tracking, and analytics SDK for Flutter applications. Track errors, monitor performance, and gain insights into your app's behavior.

example/lib/main.dart

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

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

  // Initialize Telling SDK
  // Replace 'YOUR_API_KEY' with a valid key from your dashboard
  await Telling.instance.init(
    'API_KEY',
    enableDebugLogs: true,
  );

  // Enable crash reporting
  Telling.instance.enableCrashReporting();

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Telling Logger Example',
      navigatorObservers: [
        // Track screen views automatically
        Telling.instance.screenTracker,
      ],
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const StartupScreen(),
    );
  }
}

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

  @override
  State<StartupScreen> createState() => _StartupScreenState();
}

class _StartupScreenState extends State<StartupScreen> {
  @override
  void initState() {
    super.initState();
    _checkAppVersion();
  }

  Future<void> _checkAppVersion() async {
    // Artificial delay to show the loading state (optional)
    await Future.delayed(const Duration(seconds: 1));

    if (!mounted) return;

    // Check for updates
    final result = await Telling.instance.checkVersion();
    print('Version check result: $result');

    if (!mounted) return;

    if (result.requiresUpdate) {
      // Show update dialog
      final shouldUpdate = await showDialog<bool>(
        context: context,
        barrierDismissible: !result.isRequired,
        builder: (_) => AlertDialog(
          title:
              Text(result.isRequired ? 'Update Required' : 'Update Available'),
          content: Text(result.message ?? 'A new version is available.'),
          actions: [
            if (!result.isRequired)
              TextButton(
                onPressed: () => Navigator.pop(context, false),
                child: const Text('Later'),
              ),
            ElevatedButton(
              onPressed: () => Navigator.pop(context, true),
              child: const Text('Update Now'),
            ),
          ],
        ),
      );

      if (!mounted) return;

      if (shouldUpdate == true) {
        // Log the update acceptance before opening store
        await Telling.instance.acceptUpdate(minVersion: result.minVersion);
        // Open store URL
        // launchUrl(Uri.parse(result.storeUrl!));
        return;
      }

      // User skipped (tapped "Later" or dismissed) - snooze and continue
      if (result.minVersion != null) {
        print('Snoozing update for minVersion: ${result.minVersion}');
        await Telling.instance.snoozeUpdate(
          days: 3,
          minVersion: result.minVersion!,
        );
        print('Snooze saved successfully');
      }
    }

    _navigateToLogin();
  }

  void _navigateToLogin() {
    Navigator.of(context).pushReplacement(
      MaterialPageRoute(
        builder: (_) => const LoginScreen(),
        settings: const RouteSettings(name: 'LoginScreen'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CircularProgressIndicator(),
            SizedBox(height: 16),
            Text('Checking for updates...'),
          ],
        ),
      ),
    );
  }
}

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

  @override
  State<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  final _formKey = GlobalKey<FormState>();
  final _idController = TextEditingController(text: 'user_123');
  final _nameController = TextEditingController(text: 'John Doe');
  final _emailController = TextEditingController(text: 'john@example.com');

  void _login() {
    if (_formKey.currentState!.validate()) {
      // Set user context in Telling SDK
      Telling.instance.setUser(
        userId: _idController.text,
        userName: _nameController.text,
        userEmail: _emailController.text,
      );

      // Set initial user properties
      Telling.instance.setUserProperties({
        'subscription_tier': 'free',
        'signup_date': DateTime.now().toIso8601String(),
        'platform': 'mobile',
      });

      // Navigate to Home
      Navigator.of(context).pushReplacement(
        MaterialPageRoute(
          builder: (_) => const HomeScreen(),
          settings: const RouteSettings(name: 'HomeScreen'),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Login')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Text(
                'Enter User Details',
                style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
              ).nowTelling(name: 'Login Header'),
              const SizedBox(height: 20),
              TextFormField(
                controller: _idController,
                decoration: const InputDecoration(
                  labelText: 'User ID',
                  border: OutlineInputBorder(),
                ),
                validator: (value) =>
                    value?.isEmpty ?? true ? 'Required' : null,
              ),
              const SizedBox(height: 10),
              TextFormField(
                controller: _nameController,
                decoration: const InputDecoration(
                  labelText: 'Name',
                  border: OutlineInputBorder(),
                ),
              ),
              const SizedBox(height: 10),
              TextFormField(
                controller: _emailController,
                decoration: const InputDecoration(
                  labelText: 'Email',
                  border: OutlineInputBorder(),
                ),
              ),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: _login,
                child: const Text('Set User Context & Continue'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Telling Logger Example'),
        actions: [
          IconButton(
            icon: const Icon(Icons.logout),
            onPressed: () {
              // Clear user context on logout
              Telling.instance.clearUser();
              Telling.instance.clearUserProperties();
              Navigator.of(context).pushReplacement(
                MaterialPageRoute(
                  builder: (_) => const LoginScreen(),
                  settings: const RouteSettings(name: 'LoginScreen'),
                ),
              );
            },
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            const Text(
              'Welcome to Telling Logger!',
              style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
              textAlign: TextAlign.center,
            ).nowTelling(
              name: 'Welcome Text',
              metadata: {'font_size': 20},
            ),
            const SizedBox(height: 30),

            // Logging Section
            const Text(
              'Basic Logging',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 10),
            Wrap(
              spacing: 10,
              runSpacing: 10,
              children: [
                ElevatedButton(
                  onPressed: () {
                    Telling.instance.log('Info button clicked');
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(content: Text('Logged Info')),
                    );
                  },
                  child: const Text('Log Info'),
                ),
                ElevatedButton(
                  style:
                      ElevatedButton.styleFrom(backgroundColor: Colors.orange),
                  onPressed: () {
                    Telling.instance.log(
                      'Warning: Battery low',
                      level: LogLevel.warning,
                      metadata: {'battery_level': 15},
                    );
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(content: Text('Logged Warning')),
                    );
                  },
                  child: const Text('Log Warning'),
                ),
                ElevatedButton(
                  style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
                  onPressed: () {
                    try {
                      throw Exception('Test Exception');
                    } catch (e, stack) {
                      Telling.instance.captureException(
                          error: e,
                          stackTrace: stack,
                          context: 'Test Exception');
                    }
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(content: Text('Logged Error')),
                    );
                  },
                  child: const Text('Log Error'),
                ),
              ],
            ),

            const SizedBox(height: 30),
            const Divider(),

            // User Properties Section
            Row(
              spacing: 10,
              children: [
                const Text(
                  'User Properties',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
                ElevatedButton(
                  onPressed: () {
                    Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (_) => const UserPropertiesScreen(),
                        settings:
                            const RouteSettings(name: 'UserPropertiesScreen'),
                      ),
                    );
                  },
                  child: const Text('Panel'),
                ),
              ],
            ),
            const SizedBox(height: 10),
            Wrap(
              spacing: 10,
              runSpacing: 10,
              children: [
                ElevatedButton.icon(
                  onPressed: () {
                    Telling.instance.setUserProperty(
                        key: 'subscription_tier', value: 'premium');
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(
                          content: Text('βœ… Set: subscription_tier = premium')),
                    );
                  },
                  icon: const Icon(Icons.workspace_premium, size: 18),
                  label: const Text('Set Premium'),
                ),
                ElevatedButton.icon(
                  onPressed: () {
                    Telling.instance.setUserProperty(
                        key: 'subscription_tier', value: 'enterprise');
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(
                          content:
                              Text('βœ… Set: subscription_tier = enterprise')),
                    );
                  },
                  icon: const Icon(Icons.business, size: 18),
                  label: const Text('Set Enterprise'),
                ),
                ElevatedButton.icon(
                  onPressed: () {
                    Telling.instance.setUserProperties({
                      'mrr': 299.99,
                      'seats': 10,
                      'industry': 'SaaS',
                      'plan_renewal_date': '2025-12-31',
                    });
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(
                          content: Text(
                              'βœ… Set 4 properties (mrr, seats, industry, renewal_date)')),
                    );
                  },
                  icon: const Icon(Icons.settings, size: 18),
                  label: const Text('Set Bulk Properties'),
                ),
                ElevatedButton.icon(
                  onPressed: () {
                    final tier =
                        Telling.instance.getUserProperty('subscription_tier');
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(
                          content: Text('Current tier: ${tier ?? "not set"}')),
                    );
                  },
                  icon: const Icon(Icons.info_outline, size: 18),
                  label: const Text('Get Property'),
                  style: ElevatedButton.styleFrom(backgroundColor: Colors.grey),
                ),
                ElevatedButton.icon(
                  onPressed: () {
                    Telling.instance.clearUserProperties();
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(
                          content: Text('πŸ—‘οΈ Cleared all properties')),
                    );
                  },
                  icon: const Icon(Icons.delete, size: 18),
                  label: const Text('Clear All'),
                  style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
                ),
              ],
            ),

            const SizedBox(height: 30),
            const Divider(),

            // Analytics Events Section
            const Text(
              'Analytics Events',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 10),
            Wrap(
              spacing: 10,
              runSpacing: 10,
              children: [
                ElevatedButton.icon(
                  onPressed: () {
                    Telling.instance.event(
                      'button_clicked',
                      properties: {
                        'button_name': 'Purchase',
                        'screen': 'Home',
                      },
                    );
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(content: Text('πŸ“Š Event: button_clicked')),
                    );
                  },
                  icon: const Icon(Icons.touch_app, size: 18),
                  label: const Text('Track Button Click'),
                ),
                ElevatedButton.icon(
                  onPressed: () {
                    Telling.instance.event(
                      'purchase_completed',
                      properties: {
                        'amount': 49.99,
                        'currency': 'USD',
                        'product_id': 'premium_monthly',
                      },
                    );
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(
                          content: Text('πŸ“Š Event: purchase_completed')),
                    );
                  },
                  icon: const Icon(Icons.shopping_cart, size: 18),
                  label: const Text('Track Purchase'),
                ),
                ElevatedButton.icon(
                  onPressed: () {
                    Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (_) => const CheckoutScreen(),
                        settings: const RouteSettings(name: 'CheckoutScreen'),
                      ),
                    );
                  },
                  icon: const Icon(Icons.shopping_cart, size: 18),
                  label: const Text('Checkout'),
                ),
              ],
            ),

            const SizedBox(height: 30),
            const Divider(),

            // Navigation Section
            const Text(
              'Navigation & Testing',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 10),
            Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                ElevatedButton(
                  onPressed: () {
                    Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (_) => const UserPropertiesScreen(),
                        settings:
                            const RouteSettings(name: 'UserPropertiesScreen'),
                      ),
                    );
                  },
                  child: const Text('πŸ“‹ User Properties Panel'),
                ),
                const SizedBox(height: 10),
                ElevatedButton(
                  onPressed: () {
                    Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (_) => const SecondScreen(),
                        settings: const RouteSettings(name: 'SecondScreen'),
                      ),
                    );
                  },
                  child: const Text('Go to Second Screen'),
                ),
                const SizedBox(height: 10),
                ElevatedButton(
                  style:
                      ElevatedButton.styleFrom(backgroundColor: Colors.black),
                  onPressed: () {
                    // Simulate a crash
                    throw StateError('This is a forced crash for testing!');
                  },
                  child: const Text('πŸ’₯ Force Crash'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

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

  @override
  State<UserPropertiesScreen> createState() => _UserPropertiesScreenState();
}

class _UserPropertiesScreenState extends State<UserPropertiesScreen> {
  final _keyController = TextEditingController();
  final _valueController = TextEditingController();
  final Map<String, dynamic> _currentProperties = {};

  @override
  void initState() {
    super.initState();
    // Initialize with some common properties
    _currentProperties['subscription_tier'] =
        Telling.instance.getUserProperty('subscription_tier') ?? 'free';
  }

  void _setCustomProperty() {
    if (_keyController.text.isEmpty || _valueController.text.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Please enter both key and value')),
      );
      return;
    }

    // Try to parse as number, otherwise use as string
    dynamic value = _valueController.text;
    if (double.tryParse(_valueController.text) != null) {
      value = double.parse(_valueController.text);
    }

    Telling.instance.setUserProperty(key: _keyController.text, value: value);

    setState(() {
      _currentProperties[_keyController.text] = value;
    });

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('βœ… Set: ${_keyController.text} = $value')),
    );

    _keyController.clear();
    _valueController.clear();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('User Properties Panel')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            const Text(
              'Set Custom Property',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 10),
            Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _keyController,
                    decoration: const InputDecoration(
                      labelText: 'Key',
                      border: OutlineInputBorder(),
                      hintText: 'e.g. age',
                    ),
                  ),
                ),
                const SizedBox(width: 10),
                Expanded(
                  child: TextField(
                    controller: _valueController,
                    decoration: const InputDecoration(
                      labelText: 'Value',
                      border: OutlineInputBorder(),
                      hintText: 'e.g. 25',
                    ),
                  ),
                ),
                const SizedBox(width: 10),
                ElevatedButton(
                  onPressed: _setCustomProperty,
                  child: const Text('Set'),
                ),
              ],
            ),
            const SizedBox(height: 30),
            const Text(
              'Quick Actions',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 10),
            Wrap(
              spacing: 10,
              runSpacing: 10,
              children: [
                _QuickPropertyButton(
                  label: 'Set Age: 25',
                  onPressed: () {
                    Telling.instance.setUserProperty(key: 'age', value: 25);
                    setState(() => _currentProperties['age'] = 25);
                  },
                ),
                _QuickPropertyButton(
                  label: 'Set Country: US',
                  onPressed: () {
                    Telling.instance
                        .setUserProperty(key: 'country', value: 'US');
                    setState(() => _currentProperties['country'] = 'US');
                  },
                ),
                _QuickPropertyButton(
                  label: 'Set Premium',
                  onPressed: () {
                    Telling.instance.setUserProperty(
                        key: 'subscription_tier', value: 'premium');
                    setState(() =>
                        _currentProperties['subscription_tier'] = 'premium');
                  },
                ),
                _QuickPropertyButton(
                  label: 'Set MRR: 99.99',
                  onPressed: () {
                    Telling.instance.setUserProperty(key: 'mrr', value: 99.99);
                    setState(() => _currentProperties['mrr'] = 99.99);
                  },
                ),
                _QuickPropertyButton(
                  label: 'Clear All',
                  color: Colors.red,
                  onPressed: () {
                    Telling.instance.clearUserProperties();
                    setState(() => _currentProperties.clear());
                  },
                ),
              ],
            ),
            const SizedBox(height: 30),
            const Text(
              'Current Properties (Local View)',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 10),
            Expanded(
              child: _currentProperties.isEmpty
                  ? const Center(
                      child: Text(
                        'No properties set yet',
                        style: TextStyle(color: Colors.grey),
                      ),
                    )
                  : ListView.builder(
                      itemCount: _currentProperties.length,
                      itemBuilder: (context, index) {
                        final key = _currentProperties.keys.elementAt(index);
                        final value = _currentProperties[key];
                        return Card(
                          child: ListTile(
                            leading: const Icon(Icons.label),
                            title: Text(key),
                            subtitle: Text(
                              value.toString(),
                              style:
                                  const TextStyle(fontWeight: FontWeight.bold),
                            ),
                            trailing: IconButton(
                              icon: const Icon(Icons.delete, color: Colors.red),
                              onPressed: () {
                                Telling.instance.clearUserProperty(key);
                                setState(() => _currentProperties.remove(key));
                                ScaffoldMessenger.of(context).showSnackBar(
                                  SnackBar(content: Text('Cleared: $key')),
                                );
                              },
                            ),
                          ),
                        );
                      },
                    ),
            ),
            const SizedBox(height: 16),
            Container(
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.blue.shade50,
                borderRadius: BorderRadius.circular(8),
                border: Border.all(color: Colors.blue.shade200),
              ),
              child: const Text(
                'πŸ’‘ Tip: Properties are automatically included in all log events for segmentation',
                style: TextStyle(fontSize: 12),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _QuickPropertyButton extends StatelessWidget {
  final String label;
  final VoidCallback onPressed;
  final Color? color;

  const _QuickPropertyButton({
    required this.label,
    required this.onPressed,
    this.color,
  });

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed,
      style: color != null
          ? ElevatedButton.styleFrom(backgroundColor: color)
          : null,
      child: Text(label),
    );
  }
}

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

  @override
  State<CheckoutScreen> createState() => _CheckoutScreenState();
}

class _CheckoutScreenState extends State<CheckoutScreen> {
  int _currentStep = 0;
  final _shippingController = TextEditingController();
  final _cardController = TextEditingController();

  final screenFunnelName = 'checkout_flow';

  @override
  void initState() {
    super.initState();
    // Track Step 1: Cart Viewed
    Telling.instance.trackFunnel(
      funnelName: screenFunnelName,
      stepName: 'cart_viewed',
      step: 1,
      properties: {'item_count': 2, 'total_value': 49.99},
    );
  }

  void _nextStep() {
    if (_currentStep == 0) {
      // Moving to Shipping
      setState(() => _currentStep = 1);
      Telling.instance.trackFunnel(
        funnelName: screenFunnelName,
        stepName: 'shipping_started',
        step: 2,
      );
    } else if (_currentStep == 1) {
      // Moving to Payment
      if (_shippingController.text.isNotEmpty) {
        setState(() => _currentStep = 2);
        Telling.instance.trackFunnel(
          funnelName: screenFunnelName,
          stepName: 'shipping_completed',
          step: 3,
          properties: {'address_length': _shippingController.text.length},
        );
      } else {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Please enter shipping info')),
        );
      }
    } else if (_currentStep == 2) {
      // Completing Order
      if (_cardController.text.isNotEmpty) {
        Telling.instance.trackFunnel(
          funnelName: screenFunnelName,
          stepName: 'payment_completed',
          step: 4,
          properties: {'payment_method': 'credit_card'},
        );

        // Track the final conversion event too
        Telling.instance.event('purchase_completed', properties: {
          'amount': 49.99,
          'currency': 'USD',
        });

        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('πŸŽ‰ Order Placed! Funnel Complete.')),
        );
        Navigator.of(context).pop();
      } else {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Please enter card info')),
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Checkout Flow')),
      body: Stepper(
        currentStep: _currentStep,
        onStepContinue: _nextStep,
        onStepCancel: () {
          if (_currentStep > 0) {
            setState(() => _currentStep -= 1);
          } else {
            Navigator.of(context).pop();
          }
        },
        steps: [
          Step(
            title: const Text('Review Cart'),
            content: const Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                ListTile(
                  leading: Icon(Icons.shopping_bag),
                  title: Text('Premium Plan'),
                  subtitle: Text('\$49.99 / month'),
                ),
                ListTile(
                  leading: Icon(Icons.confirmation_number),
                  title: Text('Setup Fee'),
                  subtitle: Text('\$0.00'),
                ),
                Divider(),
                Text('Total: \$49.99',
                    style: TextStyle(fontWeight: FontWeight.bold)),
              ],
            ),
            isActive: _currentStep >= 0,
            state: _currentStep > 0 ? StepState.complete : StepState.editing,
          ),
          Step(
            title: const Text('Shipping Info'),
            content: TextField(
              controller: _shippingController,
              decoration: const InputDecoration(
                labelText: 'Shipping Address',
                border: OutlineInputBorder(),
              ),
            ),
            isActive: _currentStep >= 1,
            state: _currentStep > 1 ? StepState.complete : StepState.editing,
          ),
          Step(
            title: const Text('Payment'),
            content: TextField(
              controller: _cardController,
              decoration: const InputDecoration(
                labelText: 'Card Number (Fake)',
                border: OutlineInputBorder(),
              ),
              keyboardType: TextInputType.number,
            ),
            isActive: _currentStep >= 2,
            state: StepState.editing,
          ),
        ],
      ),
    );
  }
}

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 screen demonstrates automatic screen tracking',
              textAlign: TextAlign.center,
              style: TextStyle(fontSize: 16),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => Navigator.of(context).pop(),
              child: const Text('Back to Home'),
            ),
          ],
        ),
      ),
    );
  }
}
6
likes
160
points
532
downloads

Publisher

unverified uploader

Weekly Downloads

Comprehensive crash reporting, error tracking, and analytics SDK for Flutter applications. Track errors, monitor performance, and gain insights into your app's behavior.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

device_info_plus, flutter, http, package_info_plus, shared_preferences, stack_trace, url_launcher

More

Packages that depend on telling_logger