Analytics integration in Flutter
By Ann Tech · 1 March 2026
Analytics tell you what users actually do in your app — not what you think they do. Firebase Analytics is free, deeply integrated with the Firebase ecosystem, and sufficient for most Flutter apps. Here is how to instrument it properly.
Setup
dependencies:
firebase_analytics: ^11.0.0
firebase_core: ^3.0.0
Automatic events
Firebase Analytics logs these automatically:
session_start/session_endscreen_view(when using go_router or Navigator)first_openapp_updateapp_exception(from Crashlytics)
Screen tracking
With go_router:
GoRouter(
observers: [
FirebaseAnalyticsObserver(analytics: FirebaseAnalytics.instance),
],
routes: [...],
)
Manually (when auto-tracking doesn't work):
await FirebaseAnalytics.instance.setCurrentScreen(
screenName: 'OrderDetailScreen',
screenClassOverride: 'OrderDetailScreen',
);
Custom events
// Purchase
await FirebaseAnalytics.instance.logPurchase(
transactionId: order.id,
currency: 'USD',
value: order.total,
items: order.items.map((item) => AnalyticsEventItem(
itemId: item.productId,
itemName: item.productName,
quantity: item.quantity,
price: item.unitPrice,
)).toList(),
);
// Custom events
await FirebaseAnalytics.instance.logEvent(
name: 'checkout_started',
parameters: {
'cart_size': cart.items.length,
'cart_value': cart.total,
'coupon_applied': cart.couponCode != null,
},
);
await FirebaseAnalytics.instance.logEvent(
name: 'search',
parameters: {
'search_term': query,
'result_count': results.length,
},
);
Abstraction layer
Don't call Firebase Analytics directly from widgets — abstract it so you can swap providers or add more:
abstract interface class AnalyticsService {
Future<void> logEvent(String name, {Map<String, Object>? parameters});
Future<void> logScreenView(String screenName);
Future<void> setUserId(String? userId);
Future<void> setUserProperty(String name, String? value);
}
class FirebaseAnalyticsService implements AnalyticsService {
FirebaseAnalyticsService(this._analytics);
final FirebaseAnalytics _analytics;
@override
Future<void> logEvent(String name, {Map<String, Object>? parameters}) async {
await _analytics.logEvent(name: name, parameters: parameters);
}
@override
Future<void> setUserId(String? userId) async {
await _analytics.setUserId(id: userId);
}
@override
Future<void> setUserProperty(String name, String? value) async {
await _analytics.setUserProperty(name: name, value: value);
}
}
// Null implementation for tests
class NoOpAnalyticsService implements AnalyticsService {
@override Future<void> logEvent(String name, {Map<String, Object>? parameters}) async {}
@override Future<void> logScreenView(String screenName) async {}
@override Future<void> setUserId(String? userId) async {}
@override Future<void> setUserProperty(String name, String? value) async {}
}
User properties
Segment users by properties to compare analytics across segments:
// After sign-in
await analytics.setUserId(user.id);
await analytics.setUserProperty('user_plan', user.plan); // 'free' | 'premium'
await analytics.setUserProperty('user_region', user.region);
await analytics.setUserProperty('account_age_days', user.daysSinceSignup.toString());
Funnel analysis
Instrument each step of a critical flow:
// Step 1: cart viewed
analytics.logEvent('cart_viewed', parameters: {'item_count': cart.items.length});
// Step 2: checkout started
analytics.logEvent('checkout_started', parameters: {'cart_value': cart.total});
// Step 3: payment method selected
analytics.logEvent('payment_method_selected', parameters: {'method': 'card'});
// Step 4: order placed
analytics.logEvent('order_placed', parameters: {'order_id': order.id, 'total': order.total});
In Firebase Analytics, create a funnel report from these events to see where users drop off.
Debug mode
# See events in real-time in DebugView
# Android:
adb shell setprop debug.firebase.analytics.app com.example.myapp
# iOS: add -FIRAnalyticsDebugEnabled to Xcode launch arguments
Firebase Console → Analytics → DebugView shows events within seconds.
Privacy considerations
Firebase Analytics collects device identifiers and usage patterns. Comply with privacy regulations:
// Disable analytics for users who opt out (GDPR)
await FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(hasConsent);
// Don't set userId if the user is anonymous or hasn't consented
if (hasConsent && user != null) {
await analytics.setUserId(user.id);
}
Common pitfalls
Logging PII in event parameters. Event parameter values appear in the Firebase console and can be exported. Never log email addresses, names, or other PII. Log user IDs only.
Too many custom events. Firebase Analytics supports up to 500 distinct event names per app. Don't create event names dynamically ('tap_button_${buttonId}'). Use a parameter instead: logEvent('button_tap', parameters: {'button_id': buttonId}).
Not testing analytics in isolation. Widgets should call analyticsService.logEvent(...), not FirebaseAnalytics.instance.logEvent(...) directly. The abstraction lets you inject a NoOpAnalyticsService in tests to avoid side effects.
Sign in to like, dislike, or report.