Analytics Usage Guide
Explains a multi-provider analytics system with event categories, factory methods, and runtime flags for Firebase and Matomo.
What this file does
Explains a multi-provider analytics system with event categories, factory methods, and runtime flags for Firebase and Matomo.
When to use it
- Adding analytics to a Flutter wallet app
- Implementing a multi-provider analytics architecture
- Creating structured analytics events with factory methods
- Setting up analytics with Firebase and Matomo
Assumes this stack
Analytics Usage Guide
This document describes how to use the analytics system in the application.
Overview
The analytics system is designed with a clear separation of concerns:
AnalyticsBloc- Manages the user preference for whether analytics are enabled or disabledAnalyticsRepository- Aggregates and manages multiple analytics providersAnalyticsEvents- Event data structuresAnalyticsApiproviders - Concrete providers (e.g.,FirebaseAnalyticsApi,MatomoAnalyticsApi)
Event Organization
Analytics events are organized by their functional categories:
- HD Wallet Operations: Events related to wallet address generation and management
- UI Usability: Events that track user interaction metrics
- Data Sync: Events for data synchronization and updates (e.g.,
CoinsDataUpdatedEventData) - Performance: Events that track application performance metrics
For better maintainability, events should be organized in separate files by their functional category:
lib/analytics/events/
├── user_engagement_events.dart
├── user_acquisition_events.dart
├── security_events.dart
├── portfolio_events.dart
├── transaction_events.dart
├── data_sync_events.dart
├── performance_events.dart
└── ui_usability_events.dart
Usage Examples
Initialize the analytics system
// In your dependency injection setup
final analyticsRepo = AnalyticsRepoImpl(settings);
final analyticsBloc = AnalyticsBloc(
analytics: analyticsRepo,
storedData: storedData,
repository: settingsRepo,
);
Log an analytics event
// Using the factory
analyticsRepo.logEvent(
AnalyticsEvents.appOpened(
platform: 'iOS',
appVersion: '1.0.0',
),
);
// Or directly create the event
analyticsRepo.logEvent(
AppOpenedEventData(
platform: 'iOS',
appVersion: '1.0.0',
),
);
Enable or disable analytics
// To enable analytics (via user setting)
analyticsBloc.add(const AnalyticsActivateEvent());
// To disable analytics (via user setting)
analyticsBloc.add(const AnalyticsDeactivateEvent());
Check if analytics are enabled
final isAnalyticsEnabled = analyticsBloc.state.isSendDataAllowed;
// Or from the repository
final isActive = analyticsRepo.isActive;
Creating New Analytics Events
To add a new analytics event:
-
Create an Event Data Class:
class NewFeatureEventData extends AnalyticsEventData { const NewFeatureEventData({ required this.featureId, required this.actionType, this.duration, }); final String featureId; final String actionType; final int? duration; @override String get name => 'new_feature_action'; @override JsonMap get parameters { final result = <String, Object>{ 'feature_id': featureId, 'action_type': actionType, }; if (duration != null) { result['duration'] = duration!; } return result; } } -
Add a Factory Method:
// In analytics_factory.dart class AnalyticsEvents { // ...existing factory methods... /// New feature action event static NewFeatureEventData newFeatureAction({ required String featureId, required String actionType, int? duration, }) { return NewFeatureEventData( featureId: featureId, actionType: actionType, duration: duration, ); } } -
Use the New Event:
analyticsRepo.logEvent( AnalyticsEvents.newFeatureAction( featureId: 'feature_123', actionType: 'click', duration: 250, ), );
Detailed Event Examples by Category
User Engagement Events
// E01: App opened
analyticsRepo.logEvent(
AnalyticsEvents.appOpened(
platform: 'iOS',
appVersion: '1.2.3',
),
);
User Acquisition Events
// E02: Onboarding started
analyticsRepo.logEvent(
AnalyticsEvents.onboardingStarted(
method: 'create',
referralSource: 'website',
),
);
// E03: Wallet created
analyticsRepo.logEvent(
AnalyticsEvents.walletCreated(
source: 'mobile',
walletType: 'hd_wallet',
),
);
// E04: Wallet imported
analyticsRepo.logEvent(
AnalyticsEvents.walletImported(
source: 'desktop',
importType: 'seed_phrase',
walletType: 'hd_wallet',
),
);
Security Events
// E05: Backup completed
analyticsRepo.logEvent(
AnalyticsEvents.backupCompleted(
backupTime: 120, // seconds
method: 'paper',
walletType: 'hd_wallet',
),
);
// E06: Backup skipped
analyticsRepo.logEvent(
AnalyticsEvents.backupSkipped(
stageSkipped: 'verification',
walletType: 'hd_wallet',
),
);
Portfolio Events
// E07: Portfolio viewed
analyticsRepo.logEvent(
AnalyticsEvents.portfolioViewed(
totalCoins: 5,
totalValueUsd: 1250.75,
),
);
// E08: Portfolio growth viewed
analyticsRepo.logEvent(
AnalyticsEvents.portfolioGrowthViewed(
period: '7d',
growthPct: 3.5,
),
);
// E09: Portfolio PnL viewed
analyticsRepo.logEvent(
AnalyticsEvents.portfolioPnlViewed(
timeframe: '30d',
realizedPnl: 120.50,
unrealizedPnl: 75.25,
),
);
Asset Management Events
// E10: Asset added
analyticsRepo.logEvent(
AnalyticsEvents.assetAdded(
assetSymbol: 'KMD',
assetNetwork: 'komodo',
walletType: 'hd_wallet',
),
);
// E11: Asset viewed
analyticsRepo.logEvent(
AnalyticsEvents.assetViewed(
assetSymbol: 'BTC',
assetNetwork: 'bitcoin',
walletType: 'hd_wallet',
),
);
// E12: Asset enabled
analyticsRepo.logEvent(
AnalyticsEvents.assetEnabled(
assetSymbol: 'ETH',
assetNetwork: 'ethereum',
walletType: 'hd_wallet',
),
);
// E13: Asset disabled
analyticsRepo.logEvent(
AnalyticsEvents.assetDisabled(
assetSymbol: 'XRP',
assetNetwork: 'ripple',
walletType: 'hd_wallet',
),
);
Transaction Events
// E14: Send initiated
analyticsRepo.logEvent(
AnalyticsEvents.sendInitiated(
assetSymbol: 'BTC',
network: 'bitcoin',
amount: 0.01,
walletType: 'hd_wallet',
),
);
// E15: Send succeeded
analyticsRepo.logEvent(
AnalyticsEvents.sendSucceeded(
assetSymbol: 'BTC',
network: 'bitcoin',
amount: 0.01,
walletType: 'hd_wallet',
),
);
// E16: Send failed
analyticsRepo.logEvent(
AnalyticsEvents.sendFailed(
assetSymbol: 'BTC',
network: 'bitcoin',
failReason: 'insufficient_funds',
walletType: 'hd_wallet',
),
);
Trading (DEX) Events
// E17: Swap initiated
analyticsRepo.logEvent(
AnalyticsEvents.swapInitiated(
fromAsset: 'BTC',
toAsset: 'KMD',
networks: 'bitcoin,komodo',
walletType: 'hd_wallet',
),
);
// E18: Swap succeeded
analyticsRepo.logEvent(
AnalyticsEvents.swapSucceeded(
fromAsset: 'BTC',
toAsset: 'KMD',
amount: 0.1,
fee: 0.001,
walletType: 'hd_wallet',
),
);
// E19: Swap failed
analyticsRepo.logEvent(
AnalyticsEvents.swapFailed(
fromAsset: 'BTC',
toAsset: 'KMD',
failStage: 'order_matching',
walletType: 'hd_wallet',
),
);
HD Wallet Operations
// E38: HD Address generated
analyticsRepo.logEvent(
AnalyticsEvents.hdAddressGenerated(
accountIndex: 0,
addressIndex: 5,
assetSymbol: 'BTC',
),
);
UI & Performance Events
// E40: Wallet list half viewport reached
analyticsRepo.logEvent(
AnalyticsEvents.walletListHalfViewportReached(
timeToHalfMs: 350,
walletSize: 12,
),
);
// E41: Coins data updated
analyticsRepo.logEvent(
AnalyticsEvents.coinsDataUpdated(
coinsCount: 120,
updateSource: 'api',
updateDurationMs: 450,
),
);
// E44: Page interactive delay
analyticsRepo.logEvent(
AnalyticsEvents.pageInteractiveDelay(
pageName: 'portfolio',
interactiveDelayMs: 650,
spinnerTimeMs: 450,
),
);
Best Practices for Analytics Events
-
Event Naming:
- Use snake_case for event names
- Keep names descriptive but concise
- Group related events with common prefixes (e.g.,
asset_added,asset_viewed)
-
Parameters:
- Include only necessary parameters
- Use consistent parameter names across similar events
- Consider privacy implications of each parameter
-
Event Organization:
- Group related events in the same section of code
- Document events with clear business purposes
- Include event IDs in comments (e.g.,
// E45: New feature action)
Firebase Analytics Setup
This analytics implementation uses Firebase Analytics as the default provider. To set up Firebase for your local development environment, refer to: Firebase Setup Instructions
The setup includes generating necessary configuration files for each platform (iOS, Android, web, etc.) and integrating them into the project.
Multi‑provider architecture and runtime flags
The app can send analytics to multiple providers simultaneously via AnalyticsRepository.
- Providers are registered and initialized in
AnalyticsRepository- Firebase: always registered
- Matomo: registered when both
MATOMO_URLandMATOMO_SITE_IDare provided
- Providers are enabled/disabled at runtime based on user preference and CI/privacy flags
Runtime flags (defined in lib/shared/constants.dart) and typical usage:
# Disable analytics entirely (e.g. CI, tests, privacy-first builds)
flutter run \
--dart-define=ANALYTICS_DISABLED=true
# Mark CI environment (implicitly disables analytics in code)
flutter run \
--dart-define=CI=true
# Enable Matomo provider and configure endpoint
flutter run \
--dart-define=MATOMO_URL=https://your-matomo.example.com/ \
--dart-define=MATOMO_SITE_ID=1
Notes:
- Matomo requires both
MATOMO_URLandMATOMO_SITE_ID. Without them, it stays disabled.
See the Matomo setup guide for full details: /docs/MATOMO_SETUP.md.
Queue persistence and provider activation
- Providers manage their own event queues.
- When analytics is disabled (user opt‑out or CI), events are queued.
- On activation, queued events are flushed.
- The Matomo provider periodically persists its queue to
SharedPreferencesand restores it on startup to prevent data loss across app restarts.
Data Sync Events Example
The CoinsDataUpdatedEventData class is an example of a data sync event that tracks when coin configuration data is refreshed:
// Usage example in app_bloc_root.dart
context.read<AnalyticsBloc>().add(
AnalyticsCoinsDataUpdatedEvent(
updateSource: 'remote',
updateDurationMs: stopwatch.elapsedMilliseconds,
coinsCount: coins.length,
),
);
This event:
- Tracks how many coins were updated (
coinsCount) - Records the data source (
updateSource: 'remote', 'cache', etc.) - Measures how long the update took (
updateDurationMs)
This information helps identify potential performance issues with data synchronization and ensures data freshness.
Benefits of this Architecture
-
Separation of Concerns
AnalyticsBlocfocuses only on user preferences- Event data classes define the structure of events
- Repository pattern provides a clean API
-
OOP-based Design
- Events are proper objects with behavior
- Follows Open/Closed Principle - add new events without modifying existing code
-
Flexibility
- Easy to add new events by creating new event classes
- Easy to change analytics provider by implementing a different
AnalyticsService
-
Testability
- Each component can be tested in isolation
- Mock implementations are simpler
What's inside
10 event categories, 19 code examples, 4 architecture components, 2 provider setup guides
Change this for your project
- Replace
GLEECBTC/gleec-walletwith your repository name - Replace
lib/shared/constants.dartwith your constants file path - Replace
MATOMO_URLandMATOMO_SITE_IDwith your Matomo instance details
Where it goes
Keep it in your repository where the agent or team that needs it will read it.
Worth borrowing
- Separating analytics preference management (Bloc) from event logging (Repository)
- Using factory methods on a static class to create typed event data objects
- Organizing events by functional category in separate files
Related Documents
ArbitragePro Configuration Guide: Complete Setup and Deployment
Guides you through installing, configuring, and deploying a multi-chain Rust arbitrage trading bot across EVM and Solana networks.
Mkan MVP Production Checklist
Lists over 200 tasks for launching a property rental MVP, organized by priority and timeline.
Analytics Pipeline
Documents an analytics pipeline using OpenSearch, OpenSearch Dashboards, and Nginx routing for a multi-tenant security platform.
VeeFore - Complete Project Documentation
Documents the full architecture, API, deployment, and configuration for a multi-platform social media management app with AI tools.