chain_analytics 0.1.0 copy "chain_analytics: ^0.1.0" to clipboard
chain_analytics: ^0.1.0 copied to clipboard

On-chain analytics for Flutter. Track app events on blockchain for permanent, verifiable, cost-effective analytics you own.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:chain_analytics/chain_analytics.dart';

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

  // Initialize ChainAnalytics
  await ChainAnalytics.initialize(
    ChainAnalyticsConfig(
      appId: 'chain_analytics_demo',
      chain: SupportedChain.baseSepolia,
      privateKey: 'YOUR_PRIVATE_KEY_HERE', // Replace with your burner wallet private key
      rpcUrl: 'YOUR_RPC_URL_HERE', // Base Sepolia RPC endpoint/ Base Sepolia RPC endpoint
      batchConfig: const BatchConfig(
        maxBatchSize: 10,
        maxBatchInterval: Duration(minutes: 1),
      ),
      debug: true, // Enable debug logging
      enableOfflineQueue: true,
    ),
  );

  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Chain Analytics Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const HomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _counter = 0;
  int _eventCount = 0;
  String? _lastTxHash;
  bool _isFlushing = false;

  @override
  void initState() {
    super.initState();
    _trackScreenView();
  }

  Future<void> _trackScreenView() async {
    await ChainAnalytics.screen('home');
    setState(() => _eventCount++);
  }

  Future<void> _incrementCounter() async {
    setState(() => _counter++);

    // Track button click
    await ChainAnalytics.track('button_clicked', {
      'action': 'increment',
      'counter_value': _counter,
    });

    setState(() => _eventCount++);

    // Check if batch should trigger
    if (_eventCount % 10 == 0) {
      _showBatchAlert();
    }
  }

  Future<void> _decrementCounter() async {
    setState(() => _counter--);

    // Track button click
    await ChainAnalytics.track('button_clicked', {
      'action': 'decrement',
      'counter_value': _counter,
    });

    setState(() => _eventCount++);

    // Check if batch should trigger
    if (_eventCount % 10 == 0) {
      _showBatchAlert();
    }
  }

  Future<void> _flushEvents() async {
    setState(() => _isFlushing = true);

    try {
      await ChainAnalytics.flush();

      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('Events flushed to blockchain!'),
            backgroundColor: Colors.green,
          ),
        );

        setState(() => _lastTxHash = 'Check logs for TX hash');
        _eventCount = 0;
      }
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Error flushing: $e'),
            backgroundColor: Colors.red,
          ),
        );
      }
    } finally {
      if (mounted) {
        setState(() => _isFlushing = false);
      }
    }
  }

  void _showBatchAlert() {
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Batch limit reached! Events will be sent soon.'),
          duration: Duration(seconds: 2),
        ),
      );
    }
  }

  void _showExplorer() {
    if (_lastTxHash != null && _lastTxHash!.isNotEmpty) {
      final url = SupportedChain.baseSepolia.getTransactionUrl(_lastTxHash!);

      // Copy to clipboard
      Clipboard.setData(ClipboardData(text: url));

      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Transaction URL copied to clipboard'),
          action: SnackBarAction(
            label: 'OPEN',
            onPressed: () {
              // Note: In a real app, you'd use url_launcher here
              ScaffoldMessenger.of(context).hideCurrentSnackBar();
            },
          ),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Chain Analytics Demo'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: _flushEvents,
            tooltip: 'Flush Events',
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Counter section
            Text('Counter', style: Theme.of(context).textTheme.headlineMedium),
            const SizedBox(height: 8),
            Text('$_counter', style: Theme.of(context).textTheme.displayLarge),
            const SizedBox(height: 32),

            // Buttons
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton.icon(
                  onPressed: _isFlushing ? null : _incrementCounter,
                  icon: const Icon(Icons.add),
                  label: const Text('Increment'),
                ),
                const SizedBox(width: 16),
                ElevatedButton.icon(
                  onPressed: _isFlushing ? null : _decrementCounter,
                  icon: const Icon(Icons.remove),
                  label: const Text('Decrement'),
                ),
              ],
            ),
            const SizedBox(height: 48),

            // Analytics info section
            Container(
              margin: const EdgeInsets.all(16),
              padding: const EdgeInsets.all(16),
              decoration: BoxDecoration(
                color: Theme.of(context).colorScheme.surfaceContainerHighest,
                borderRadius: BorderRadius.circular(12),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Blockchain Analytics',
                    style: Theme.of(context).textTheme.titleLarge,
                  ),
                  const SizedBox(height: 16),
                  _buildStatRow('Events tracked', '$_eventCount'),
                  const SizedBox(height: 8),
                  _buildStatRow('Last TX Hash', _lastTxHash ?? 'None yet'),
                  if (_lastTxHash != null)
                    TextButton(
                      onPressed: _showExplorer,
                      child: const Text('View on Explorer'),
                    ),
                  const SizedBox(height: 16),
                  SizedBox(
                    width: double.infinity,
                    child: ElevatedButton.icon(
                      onPressed: _isFlushing ? null : _flushEvents,
                      icon: _isFlushing
                          ? const SizedBox(
                              width: 16,
                              height: 16,
                              child: CircularProgressIndicator(strokeWidth: 2),
                            )
                          : const Icon(Icons.cloud_upload),
                      label: Text(
                        _isFlushing ? 'Sending...' : 'Flush to Blockchain',
                      ),
                    ),
                  ),
                ],
              ),
            ),

            // Info text
            Padding(
              padding: const EdgeInsets.all(16),
              child: Text(
                'Each button click is tracked and will be sent to Base Sepolia',
                style: Theme.of(context).textTheme.bodySmall,
                textAlign: TextAlign.center,
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildStatRow(String label, String value) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Text(label, style: Theme.of(context).textTheme.bodyMedium),
        Text(
          value,
          style: Theme.of(
            context,
          ).textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold),
        ),
      ],
    );
  }
}
0
likes
160
points
15
downloads

Publisher

unverified uploader

Weekly Downloads

On-chain analytics for Flutter. Track app events on blockchain for permanent, verifiable, cost-effective analytics you own.

Repository (GitHub)
View/report issues

Topics

#analytics #blockchain #web3 #flutter #ethereum

Documentation

API reference

License

MIT (license)

Dependencies

archive, crypto, flutter, flutter_secure_storage, http, path, sqflite, uuid, wallet, web3dart

More

Packages that depend on chain_analytics