Skip to main content

Overview

leancode_analytics_firebase provides a Firebase Analytics backend for the LeanCode analytics system. It implements the LeanAnalytics interface from leancode_analytics_base and includes automatic screen view tracking through NavigatorObserver.
This package requires Firebase to be initialized in your app before use. Make sure to set up Firebase in your project first.

Features

  • Firebase Analytics integration using the official firebase_analytics package
  • Automatic screen view tracking via FirebaseAnalyticsNavigationObserver
  • Support for custom analytics events
  • Type-safe event tracking with built-in events (Tap, Login)
  • Tracks navigation actions (push, pop, replace)

Installation

1

Add dependencies

Add the package to your pubspec.yaml:
pubspec.yaml
dependencies:
  leancode_analytics_firebase: 0.1.0+1
  firebase_core: ^3.13.0
2

Install packages

Run the Flutter package manager:
flutter pub get
3

Configure Firebase

Follow the Firebase setup guide to add Firebase to your Flutter app

Configuration

Basic Setup

Initialize Firebase and create a FirebaseLeanAnalytics instance:
main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:leancode_analytics_firebase/leancode_analytics_firebase.dart';
import 'firebase_options.dart'; // Generated by FlutterFire CLI

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Initialize Firebase
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  
  // Create analytics instance
  final analytics = FirebaseLeanAnalytics();
  
  runApp(MyApp(analytics: analytics));
}

Integrate with MaterialApp

Add the navigator observers to enable automatic screen tracking:
class MyApp extends StatelessWidget {
  const MyApp({required this.analytics, super.key});
  
  final FirebaseLeanAnalytics analytics;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      navigatorObservers: analytics.navigatorObservers,
      home: const HomeScreen(),
    );
  }
}

Using with GoRouter

final router = GoRouter(
  observers: analytics.navigatorObservers,
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomeScreen(),
    ),
  ],
);

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: router,
    );
  }
}

Tracking Events

Using Built-in Events

import 'package:leancode_analytics_firebase/leancode_analytics_firebase.dart';

// Track button taps
analytics.register(
  TapAnalyticsEvent(
    key: 'submit_button',
    label: 'Submit Form',
  ),
);

// With additional parameters
analytics.register(
  TapAnalyticsEvent(
    key: 'product_card',
    label: 'iPhone 15',
    params: {
      'product_id': 'prod_123',
      'category': 'electronics',
    },
  ),
);

Creating Custom Events

Extend AnalyticsEvent for domain-specific events:
class AddToCartEvent extends AnalyticsEvent {
  AddToCartEvent({
    required String productId,
    required String productName,
    required double price,
    int quantity = 1,
  }) : super(
    name: 'add_to_cart',
    params: {
      'product_id': productId,
      'product_name': productName,
      'price': price,
      'quantity': quantity,
    },
  );
}

// Usage
analytics.register(
  AddToCartEvent(
    productId: 'prod_123',
    productName: 'Wireless Headphones',
    price: 79.99,
    quantity: 2,
  ),
);

Automatic Screen Tracking

The FirebaseAnalyticsNavigationObserver automatically tracks screen views for routes implementing LeanAnalyticsRoute.

Implementing LeanAnalyticsRoute

class ProductDetailsRoute extends GoRoute implements LeanAnalyticsRoute {
  ProductDetailsRoute()
      : super(
          path: '/product/:id',
          builder: (context, state) {
            final productId = state.pathParameters['id']!;
            return ProductDetailsScreen(productId: productId);
          },
        );

  @override
  String get id => 'product_details_screen';

  @override
  Map<String, dynamic>? get analyticsParams => null;
}

Screen View Types

The observer tracks different navigation actions:
lib/src/firebase_analytics_observer.dart
enum ScreenViewType {
  /// A new route was pushed onto the navigator.
  push,
  
  /// A route was popped from the navigator.
  pop,
}
Each screen view event includes an action_type parameter indicating how the user navigated to the screen.

Implementation Details

FirebaseLeanAnalytics Class

The main implementation class:
lib/src/firebase_analytics.dart
class FirebaseLeanAnalytics implements LeanAnalytics {
  static final FirebaseAnalytics _instance = FirebaseAnalytics.instance;

  @override
  Future<void> register(AnalyticsEvent event) async {
    await _instance.logEvent(
      name: event.name,
      parameters: event.params,
    );
  }

  @override
  List<NavigatorObserver> get navigatorObservers =>
      [FirebaseAnalyticsNavigationObserver()];
}
Tracks screen views on navigation events:
lib/src/firebase_analytics_observer.dart
class FirebaseAnalyticsNavigationObserver extends NavigatorObserver {
  @override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
    if (route case final LeanAnalyticsRoute route) {
      _instance.logScreenView(
        screenName: route.id,
        parameters: {
          'action_type': ScreenViewType.push.name,
        },
      );
    }
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
    if (previousRoute case final LeanAnalyticsRoute previousRoute?) {
      _instance.logScreenView(
        screenName: previousRoute.id,
        parameters: {
          'action_type': ScreenViewType.pop.name,
        },
      );
    }
  }
  
  // Also implements didRemove and didReplace...
}
When a route is popped, we track the previousRoute because that’s the screen the user is now viewing. This ensures accurate screen view tracking when users navigate backwards.

Advanced Usage

Multiple Analytics Instances

class CompositeAnalytics implements LeanAnalytics {
  CompositeAnalytics(this._backends);
  
  final List<LeanAnalytics> _backends;

  @override
  void register(AnalyticsEvent event) {
    for (final backend in _backends) {
      backend.register(event);
    }
  }

  @override
  List<NavigatorObserver> get navigatorObservers =>
      _backends.expand((b) => b.navigatorObservers).toList();
}

// Use multiple backends
final analytics = CompositeAnalytics([
  FirebaseLeanAnalytics(),
  PostHogLeanAnalytics(),
]);

Conditional Tracking

class ConditionalAnalytics implements LeanAnalytics {
  ConditionalAnalytics(this._delegate, this._shouldTrack);
  
  final LeanAnalytics _delegate;
  final bool Function() _shouldTrack;

  @override
  void register(AnalyticsEvent event) {
    if (_shouldTrack()) {
      _delegate.register(event);
    }
  }

  @override
  List<NavigatorObserver> get navigatorObservers =>
      _delegate.navigatorObservers;
}

// Disable analytics in debug mode
final analytics = ConditionalAnalytics(
  FirebaseLeanAnalytics(),
  () => kReleaseMode,
);

Testing

Mocking Analytics

class MockAnalytics implements LeanAnalytics {
  final events = <AnalyticsEvent>[];

  @override
  void register(AnalyticsEvent event) {
    events.add(event);
  }

  @override
  List<NavigatorObserver> get navigatorObservers => [];
}

// In tests
void main() {
  test('tracks login event', () {
    final analytics = MockAnalytics();
    final service = AuthService(analytics);
    
    service.login('user_123');
    
    expect(analytics.events, hasLength(1));
    expect(analytics.events.first.name, 'login');
    expect(analytics.events.first.params['user_id'], 'user_123');
  });
}

Package Information

  • Version: 0.1.0+1
  • Minimum Dart SDK: 3.5.0
  • Minimum Flutter: 3.24.0
  • Dependencies:
    • firebase_analytics: ^11.4.5
    • firebase_core: ^3.13.0
    • leancode_analytics_base: 0.1.0
  • Repository: flutter_corelibrary

leancode_analytics_base

Core analytics abstractions and interfaces

leancode_analytics_posthog

PostHog analytics backend implementation

Build docs developers (and LLMs) love