DEV Community

Cover image for Flutter Analytics Like Production
Abdur Rafay Saleem
Abdur Rafay Saleem

Posted on

Flutter Analytics Like Production

Part 1: Building a Robust Analytics Service in Flutter

Analytics is a crucial part of any production application, helping teams understand user behavior, track important metrics, and make data-driven decisions. However, implementing analytics properly requires careful architectural planning to ensure maintainability, extensibility, and clean separation of concerns.

There are several tutorials out there showing how to setup analytics in flutter but lets be honest, they are all just a mess of random buggy functions and lead to spagetti code. Moreoever, they are so strongly focused on one platform that it leads to more painful refactoring down the line for adding more services. In reality, such tutorials are of no great value while making production quality apps.

In this series, we'll build a robust analytics system for Flutter applications that can handle multiple analytics providers while maintaining clean, maintainable code. This is how it is done by professionals in the industry and how you should do it too.

We'll start by establishing a solid foundation with a base analytics client.

The Analytics Contract

At the heart of our analytics architecture is the AnalyticsClientBase abstract class. This class serves as a contract that all analytics implementations must follow:

abstract class AnalyticsClientBase {
  Future<void> toggleAnalyticsCollection(bool enabled);
  Future<void> identifyUser({
    required String userId,
    required String email,
    required String role,
  });
  Future<void> resetUser();
  // ... other methods
}
Enter fullscreen mode Exit fullscreen mode

This abstract class defines a comprehensive set of tracking methods that cover common analytics scenarios:

  • User identification and session management
  • Screen views and navigation tracking
  • UI interaction events (button presses, dialogs, bottom sheets)
  • App lifecycle events (foreground/background)
  • Permission tracking
  • Custom events with additional data

Here is the full code:

abstract class AnalyticsClientBase {
/// Toggles the collection of analytics data.
Future<void> toggleAnalyticsCollection(bool enabled);
/// Identifies the user with the given [userId].
Future<void> identifyUser({
required String userId,
required String email,
required String role,
});
/// Resets the user.
Future<void> resetUser();
/// Tracks a generic event with the given [eventName] and [eventData].
Future<void> trackEvent(String eventName, [Map<String, Object>? eventData]);
/// Tracks a screen view with the given [routeName] and [action].
Future<void> trackScreenView(String routeName, String action);
/// Tracks a bottom sheet view with the given [routeName] and [data].
Future<void> trackBottomSheetView(String routeName, [Map<String, Object>? data]);
/// Tracks a dialog view with the given [dialogName] and [data].
Future<void> trackDialogView(String dialogName, [Map<String, Object>? data]);
/// Tracks the app being foregrounded.
Future<void> trackAppForegrounded();
/// Tracks the app being backgrounded.
Future<void> trackAppBackgrounded();
/// Tracks a button press with the given [buttonName] and [data].
Future<void> trackButtonPressed(String buttonName, [Map<String, Object>? data]);
/// Tracks the permission being granted with status.
Future<void> trackPermissionRequest(String permission, String status);
/// Tracks the onboarding screen view.
Future<void> trackNewAppOnboarding();
/// Tracks the creation of an app.
Future<void> trackAppCreated();
/// Tracks the update of an app.
Future<void> trackAppUpdated();
/// Tracks the deletion of an app.
Future<void> trackAppDeleted();
/// Tracks the completion of a task with the given [completedCount].
Future<void> trackTaskCompleted(int completedCount);
}

Why Use an Abstract Base Class?

The AnalyticsClientBase serves several important purposes:

  1. Contract Enforcement: Any new analytics implementation must implement all required tracking methods, ensuring consistency across different providers.
  2. Type Safety: The abstract class provides compile-time type checking and clear method signatures.
  3. Dependency Inversion: Our application code depends on the abstract interface rather than concrete implementations.
  4. Easy Integration: New analytics providers can be added by simply implementing the base class, without changing existing code.

For example, if we want to track a button press, our UI code only needs to know about the abstract interface:

void onButtonPressed(AnalyticsClientBase aClient) {
  aClient.trackButtonPressed('submit_button', {'screen': 'checkout'});
}
Enter fullscreen mode Exit fullscreen mode

This code remains unchanged regardless of whether we're using Firebase Analytics, PostHog, or any other analytics provider.

Key Methods Explained

Let's look at some key methods in our analytics contract:

  • identifyUser: Associates analytics events with a specific user, typically called after login
  • resetUser: Clears user association, usually called during logout
  • trackScreenView: Monitors navigation patterns with additional context about the action
  • trackButtonPressed: Used to log button interactions by the user. We can pass in a button name and other things as data like screen, enable/disable etc. This way we add a lot of insightful context to the event.
  • trackEvent: A flexible method for custom events with optional key-value data

Similarly, we have many more methods for some common tasks. You can adjust these as per your own requirements or add more if you want to track more things like scroll_event, swipe_event etc.

Each method is designed to be future-proof and flexible enough to work with various analytics providers.

Coming Up Next

In Part 2: Implementing Logger and Server Analytics, we'll create our first two concrete implementations of this contract:

  • A debug logger client for development
  • A server-side analytics client for custom backend tracking

We'll see how these implementations leverage our base class while providing their own unique functionality.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more