Version: 1.1.0 Major changes: - Implemented complete Flutter app structure with all core features - Added comprehensive UI screens for auth, countdown, goals, profile, settings, and social features - Integrated Supabase backend with authentication and data repositories - Added offline support with Hive caching and local storage - Implemented comprehensive routing with go_router - Added location services with Google Maps integration - Implemented notifications and home widget support - Added voice recording capabilities and AI chat features - Created comprehensive test suite and documentation - Added Android and iOS platform configurations - Implemented achievements system and social features - Added calendar integration and bucket list functionality This represents a complete Phase 1 milestone with 3,775 additions across 31 files.
15 KiB
App Performance Monitoring & Analytics Setup Guide
Project: LifeTimer
Version: 1.0.0
Date: 2026-01-03
Overview
This guide covers setting up production-ready analytics and performance monitoring for the LifeTimer app. The current implementation includes a placeholder analytics service that needs to be replaced with a real analytics provider.
Recommended Solutions
1. Firebase Analytics (Primary Recommendation)
Why Firebase Analytics?
- Free tier with generous limits
- Seamless integration with Flutter
- Real-time analytics
- User properties and event tracking
- Integration with Firebase Crashlytics
- Works well with Supabase
Setup Steps:
Step 1: Add Dependencies
Add to pubspec.yaml:
dependencies:
firebase_core: ^2.24.2
firebase_analytics: ^10.7.4
firebase_crashlytics: ^3.4.9
firebase_performance: ^0.9.3+11
Step 2: Initialize Firebase
Create lib/bootstrap/firebase.dart:
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:firebase_performance/firebase_performance.dart';
import 'package:flutter/foundation.dart';
Future<void> initializeFirebase() async {
await Firebase.initializeApp();
// Initialize Analytics
FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true);
// Initialize Crashlytics
FlutterError.onError = (errorDetails) {
FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails);
};
PlatformDispatcher.instance.onError = (error, stack) {
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
return true;
};
// Initialize Performance Monitoring
await FirebasePerformance.instance.setPerformanceCollectionEnabled(true);
}
Update lib/bootstrap/bootstrap.dart:
Future<void> bootstrap() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeFirebase();
await Supabase.initialize(
url: Env.supabaseUrl,
anonKey: Env.supabaseAnonKey,
);
initializeSupabaseClient();
}
Step 3: Update Analytics Service
Update lib/core/services/analytics_service.dart:
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
class AnalyticsService {
static final AnalyticsService _instance = AnalyticsService._internal();
factory AnalyticsService() => _instance;
AnalyticsService._internal();
final FirebaseAnalytics _analytics = FirebaseAnalytics.instance;
final FirebaseCrashlytics _crashlytics = FirebaseCrashlytics.instance;
bool _isInitialized = false;
Future<void> initialize() async {
_isInitialized = true;
await _analytics.setAnalyticsCollectionEnabled(true);
}
void setUserId(String userId) {
_analytics.setUserId(id: userId);
}
void setUserProperty(String name, dynamic value) {
_analytics.setUserProperty(name: name, value: value.toString());
}
void logEvent(String eventName, {Map<String, dynamic>? parameters}) {
_analytics.logEvent(
name: eventName,
parameters: parameters?.map(
(key, value) => MapEntry(key, value.toString()),
),
);
}
void logSignUp({required String method}) {
_analytics.logSignUp(signUpMethod: method);
}
void logSignIn({required String method}) {
_analytics.logLogin(loginMethod: method);
}
void logSignOut() {
// Firebase doesn't have a built-in sign out event
logEvent('sign_out');
}
void logGoalCreated({required String goalId, required String hasLocation, required String hasImage}) {
logEvent('goal_created', parameters: {
'goal_id': goalId,
'has_location': hasLocation,
'has_image': hasImage,
});
}
void logGoalUpdated({required String goalId}) {
logEvent('goal_updated', parameters: {'goal_id': goalId});
}
void logGoalCompleted({required String goalId, required int daysInChallenge}) {
logEvent('goal_completed', parameters: {
'goal_id': goalId,
'days_in_challenge': daysInChallenge,
});
}
void logGoalDeleted({required String goalId}) {
logEvent('goal_deleted', parameters: {'goal_id': goalId});
}
void logCountdownStarted({required String startDate, required String endDate}) {
logEvent('countdown_started', parameters: {
'start_date': startDate,
'end_date': endDate,
});
}
void logCountdownViewed() {
_analytics.logScreenView(screenName: 'home_countdown');
}
void logProfileUpdated({required String fieldsUpdated}) {
logEvent('profile_updated', parameters: {
'fields_updated': fieldsUpdated,
});
}
void logProfileVisibilityChanged({required bool isPublic}) {
logEvent('profile_visibility_changed', parameters: {
'is_public': isPublic,
});
}
void logOnboardingCompleted() {
logEvent('onboarding_completed');
}
void logOnboardingStepCompleted({required String stepName}) {
logEvent('onboarding_step_completed', parameters: {
'step_name': stepName,
});
}
void logSettingsChanged({required String settingName, required String value}) {
logEvent('settings_changed', parameters: {
'setting_name': settingName,
'value': value,
});
}
void logNotificationEnabled({required String notificationType}) {
logEvent('notification_enabled', parameters: {
'notification_type': notificationType,
});
}
void logNotificationDisabled({required String notificationType}) {
logEvent('notification_disabled', parameters: {
'notification_type': notificationType,
});
}
void logError({required String error, String? context}) {
_crashlytics.recordError(
error,
null,
fatal: false,
information: context != null ? [context] : null,
);
}
void logScreenView({required String screenName}) {
_analytics.logScreenView(screenName: screenName);
}
void reset() {
_analytics.resetAnalyticsData();
}
}
Step 4: Configure Firebase Console
- Go to Firebase Console
- Create a new project: "LifeTimer"
- Add Android app:
- Package name:
com.lifetimer.app - Download
google-services.json - Place in
android/app/
- Package name:
- Add iOS app:
- Bundle ID:
com.lifetimer.app - Download
GoogleService-Info.plist - Place in
ios/Runner/
- Bundle ID:
Step 5: Configure Android
Update android/build.gradle:
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.4.0'
}
}
Update android/app/build.gradle:
apply plugin: 'com.google.gms.google-services'
Step 6: Configure iOS
Update ios/Runner/Info.plist:
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
2. Alternative: Mixpanel Analytics
Why Mixpanel?
- Advanced user segmentation
- Funnel analysis
- Cohort analysis
- Real-time insights
Setup Steps:
Add to pubspec.yaml:
dependencies:
mixpanel_flutter: ^2.2.0
Initialize in lib/bootstrap/bootstrap.dart:
import 'package:mixpanel_flutter/mixpanel_flutter.dart';
Future<void> initializeMixpanel() async {
final mixpanel = await Mixpanel.init('YOUR_MIXPANEL_TOKEN');
mixpanel.track('app_opened');
}
3. Alternative: Sentry (Error Tracking)
Why Sentry?
- Excellent error tracking
- Performance monitoring
- Release tracking
- Breadcrumbs
Setup Steps:
Add to pubspec.yaml:
dependencies:
sentry_flutter: ^7.14.0
Initialize in lib/bootstrap/bootstrap.dart:
import 'package:sentry_flutter/sentry_flutter.dart';
Future<void> initializeSentry() async {
await SentryFlutter.init(
(options) {
options.dsn = 'YOUR_SENTRY_DSN';
options.tracesSampleRate = 1.0;
options.profilesSampleRate = 1.0;
},
);
}
Recommended Implementation
Primary Stack:
- Firebase Analytics (user analytics)
- Firebase Crashlytics (crash reporting)
- Firebase Performance Monitoring (performance)
Optional Add-ons:
- Sentry (additional error tracking)
- Mixpanel (advanced user analytics)
Key Events to Track
User Acquisition
app_opened- First app opensign_up- New user registrationsign_in- User loginsign_out- User logout
Core Features
onboarding_completed- User finishes onboardingbucket_list_created- User creates bucket listcountdown_started- User starts countdowngoal_created- User adds a goalgoal_updated- User updates a goalgoal_completed- User completes a goalgoal_deleted- User deletes a goal
Engagement
countdown_viewed- User views countdowngoals_viewed- User views goals listprofile_viewed- User views profilesocial_feed_viewed- User views social feedleaderboards_viewed- User views leaderboards
Settings
settings_changed- User changes settingsnotification_enabled- User enables notificationsnotification_disabled- User disables notificationstheme_changed- User changes theme
Errors
error- Any error occurrencenetwork_error- Network-related errorsauth_error- Authentication errors
User Properties to Track
user_id- Unique user identifiersign_up_method- Email, Google, or Applecountdown_started- Boolean, has countdown startedcountdown_start_date- Date countdown startedgoals_count- Number of goalscompleted_goals_count- Number of completed goalsis_public_profile- Profile visibilitytheme_preference- Light, dark, or systemnotification_enabled- Notifications status
Performance Metrics to Monitor
App Performance
- App startup time
- Screen load time
- API response times
- Database query times
- Image loading times
User Experience
- Crash-free users
- ANR (Application Not Responding) rate
- Session duration
- Screen flow analysis
- Drop-off points
Business Metrics
- Daily Active Users (DAU)
- Monthly Active Users (MAU)
- Retention rate (Day 1, 7, 30)
- Conversion rate (signup to countdown start)
- Goal completion rate
Logging Framework
Replace print() statements with proper logging:
Add to pubspec.yaml:
dependencies:
logger: ^2.0.2+1
Create lib/core/utils/logger.dart:
import 'package:logger/logger.dart';
final logger = Logger(
printer: PrettyPrinter(
methodCount: 2,
errorMethodCount: 8,
lineLength: 120,
colors: true,
printEmojis: true,
printTime: true,
),
);
final loggerNoStack = Logger(
printer: PrettyPrinter(
methodCount: 0,
errorMethodCount: 0,
lineLength: 120,
colors: true,
printEmojis: true,
printTime: true,
),
);
Usage:
// Instead of print('Analytics not initialized');
logger.w('Analytics not initialized');
// Instead of print('Analytics Event: $eventData');
logger.d('Analytics Event: $eventData');
// For errors
logger.e('Error syncing mutation', error: e, stackTrace: stackTrace);
Privacy & Compliance
Data Collection
- Only collect necessary data
- Anonymize user IDs where possible
- Provide opt-out options
- Follow GDPR and CCPA guidelines
User Consent
- Add consent dialog on first launch
- Allow users to opt out of analytics
- Provide privacy policy link
- Implement data deletion on request
Firebase Configuration
// Disable analytics collection for users who opt out
await FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(false);
// Delete user data on account deletion
await FirebaseAnalytics.instance.resetAnalyticsData();
Testing Analytics
Local Testing
- Use Firebase DebugView for real-time event verification
- Test all event tracking flows
- Verify user properties are set correctly
- Test error reporting
DebugView Setup
// Enable DebugView for development
await FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true);
Event Verification
// Test event tracking
AnalyticsService().logEvent('test_event', parameters: {'test': 'value'});
Monitoring Dashboards
Firebase Console Dashboards
- Overview Dashboard - Key metrics overview
- Events Dashboard - Event tracking
- Conversions Dashboard - Funnel analysis
- Audiences Dashboard - User segments
- Retention Dashboard - User retention
Custom Dashboards
Create custom dashboards for:
- Countdown start funnel
- Goal completion rate
- User engagement metrics
- Error rates
- Performance metrics
Alerts & Notifications
Set Up Alerts
- Crash Rate Alert - Notify when crash rate exceeds threshold
- Error Rate Alert - Notify when error rate spikes
- Performance Alert - Notify when app performance degrades
- User Drop-off Alert - Notify when drop-off increases
Alert Configuration
- Set appropriate thresholds
- Configure notification channels (email, Slack, etc.)
- Define escalation procedures
- Document alert responses
Documentation
Analytics Documentation
- Document all events tracked
- Document user properties
- Document funnels and conversions
- Maintain analytics dictionary
Team Training
- Train team on analytics tools
- Establish analytics review process
- Create analytics best practices guide
- Regular analytics reviews
Implementation Checklist
Firebase Setup
- Create Firebase project
- Add Android app to Firebase
- Add iOS app to Firebase
- Download configuration files
- Add dependencies to pubspec.yaml
- Initialize Firebase in bootstrap
- Configure Android build files
- Configure iOS Info.plist
- Test Firebase integration
Analytics Implementation
- Update AnalyticsService with Firebase
- Implement all event tracking
- Set user properties
- Test event tracking
- Verify in Firebase DebugView
Crashlytics Setup
- Initialize Crashlytics
- Configure error reporting
- Test crash reporting
- Verify in Firebase Console
Performance Monitoring
- Initialize Performance Monitoring
- Add custom traces
- Monitor app startup
- Monitor screen loads
- Monitor API calls
Logging Framework
- Add logger dependency
- Create logger utility
- Replace all print() statements
- Configure log levels
Privacy & Compliance
- Add consent dialog
- Implement opt-out functionality
- Update privacy policy
- Test data deletion
Monitoring & Alerts
- Set up Firebase dashboards
- Create custom dashboards
- Configure alerts
- Test alert notifications
Documentation
- Document all events
- Create analytics dictionary
- Train team
- Establish review process
Next Steps:
- Set up Firebase project
- Add Firebase dependencies
- Implement Firebase Analytics
- Replace placeholder analytics service
- Set up Crashlytics
- Implement logging framework
- Test all tracking
- Configure monitoring dashboards
- Set up alerts
- Document analytics implementation