Представьте себе ситуацию: вы рассказали всем своим родным и близким о своем замечательном новом приложении. Возможно, вам даже посчастливилось сформировать список ожидания из заинтересованных пользователей. Вы сгораете от нетерпения нажать кнопку «Publish».

Но погодите минутку…

А как вы узнаете, что действительно работает, а что нуждается в улучшении?

  • Сколько пользователей завершили онбординг?

  • Сколько пользователей зарегистрировались и создали учетную запись?

  • Сколько пользователей достигли пейвола и сколько конвертировались?

Без аналитики вы действуете вслепую.

Вы принимаете решения о продукте, основываясь на предположениях, а не на данных. Это самый простой способ замедлить ваш рост, потратить впустую время разработки или, что еще хуже, потерять пользователей.

Важность аналитики

Аналитика предоставляет вам данные, необходимые для достижения успеха. Она помогает вам:

  • Измерять вовлеченность: Кто использует ваше приложение, как часто и как долго?

  • Понимать ретеншн: Где пользователи отваливаются и почему?

  • Отслеживать использование функций: Что популярно? Что игнорируется?

  • Оптимизировать прибыль: Отслеживание покупок, подписок и оттока.

  • Знать своих пользователей: Демография, устройства, платформы.

  • Контролировать пользовательский опыт: Составление карты пользовательского опыта и выявление проблемных областей.

Получив эту информацию вы перестанете гадать и начнете принимать обоснованные решения о продукте, которые действительно улучшат ваше приложение.

Пример дашборда Mixpanel, на котором отображены ключевые пользовательские метрики моего приложения Flutter Tips
Пример дашборда Mixpanel, на котором отображены ключевые пользовательские метрики моего приложения Flutter Tips

Чему вы научитесь

В этой статье я покажу вам, как эффективно отслеживать аналитику в вашем Flutter‑приложении — от базового логирования событий до масштабируемой архитектуры, которая будет работать с разными системами, такими как Firebase и Mixpanel.

Ключевые моменты, которые мы рассмотрим:

  • Что стоит отслеживать: Выбор ключевых событий и их значимость.

  • Как структурировать аналитику: Простые и масштабируемые архитектуры для отслеживания событий.

  • Настройка Firebase Analytics: Как настроить аналитику в реальном приложении.

Что ж, давайте приступим.

Эта статья познакомит вас с основами. Если вы хотите углубить свои знания, я рекомендую ознакомиться с курсом "Flutter Mobile Developer".

Введение в отслеживание событий

Если вы спросите пять разных разработчиков, как они внедрили аналитику, вы, вероятно, получите пять разных ответов.

Как это часто бывает в программной инженерии, не существует универсального решения — только компромиссы.

Поэтому, прежде чем мы углубимся в код, давайте немного отдалимся и ответим на несколько ключевых вопросов:

  • Какие события нам следует отслеживать?

  • Как мы должны их отслеживать?

  • Каким требованиям должно соответствовать наше решение?

Как только мы разберемся с этим, мы сможем выбрать подходящую архитектуру.

Какие события нам нужно отслеживать?

Говоря простым языком, события представляют собой взаимодействия между пользователем и вашим приложением.

Давайте рассмотрим в качестве примера мое приложение Flutter Ship:

Демо‑версия приложения Flutter Ship

Приложение поможет вам «поставить все галочки» перед выпуском приложений Flutter. По сути, это предварительно заполненный список дел.

Задача этого приложения заключается в том, чтобы помочь вам выполнить все необходимые шаги перед релизом ваших Flutter‑приложений. По сути, это просто готовый список задач: https://bizz84.github.io/flutter_ship_app_web/#apps [Откройте в отдельном окне]

Если бы вам нужно было добавить аналитику в это приложение, какие события вы бы отслеживали?

Важно сосредоточиться на событиях, которые:

  • Помогают пользователю добиться успеха (например, выполнить задачи)

  • Способствуют успеху вашего бизнеса (например, регистрация пользователей, ретеншн, монетизация)

Для этого приложения значимыми событиями являются:

  1. Создание нового приложения

  2. Внесение изменений в существующее приложение

  3. Удаление приложения

  4. Выполнение задачи

Остальные события не являются обязательными для отслеживания. Не стоит отслеживать все подряд только потому что вы можете это сделать.


Отслеживание событий с помощью Firebase Analytics и Mixpanel

Теперь давайте рассмотрим, как вы можете отслеживать различные события в своем Flutter‑приложении.

Если вы используете пакет firebase_analytics, вы можете реализовать это следующим образом:

final analytics = FirebaseAnalytics.instance;
analytics.logEvent('app_created');
analytics.logEvent('app_updated');
analytics.logEvent('app_deleted');
analytics.logEvent('task_completed', parameters: {'count': count});

Или, если вы предпочитаете пакет mixpanel_flutter, то это может выглядеть так:

final mixpanel = await Mixpanel.init(
  Env.mixpanelProjectToken,
  trackAutomaticEvents: true,
);
mixpanel.track('App Created');
mixpanel.track('App Updated');
mixpanel.track('App Deleted');
mixpanel.track('Task Completed', properties: {'count': count});

Проблема сразу же бросается в глаза: разные API — разный синтаксис.

❌ Разбрасывать эти вызовы по всей вашей кодовой базе — не лучшая идея. ❌

Давайте разберемся, почему:

  • Вы привязываете себя к определенным SDK

  • Вы повсюду дублируете названия событий (легко сделать опечатку, трудно рефакторить)

  • Вы теряете типобезопасность и поддержку IDE

  • Вы не можете повторно использовать логику или отправлять события сразу нескольким вендорам

  • Вы смешиваете бизнес‑логику с деталями реализации

Мы можем добиться гораздо большего, если определим четкий интерфейс, который отделит отслеживание событий от всего остального.

Но для этого нам потребуется сформировать список требований.

Аналитика приложения: Требования

Прежде чем приступить к реализации, давайте четко сформулируем требования к аналитической системе.

Ниже представлен ряд ключевых требований, которые нам необходимо учесть:

  1. Разделение ответственности: Код приложения не должен напрямую взаимодействовать с SDK‑пакетами вендоров (например, Firebase или Mixpanel). Вместо этого все отслеживание событий будет происходить через простой и понятный интерфейс.

  2. Поддержка нескольких клиентов: Нам необходимо иметь возможность отправлять события сразу нескольким вендорам. Например, мы можем захотеть реализовать логирование одновременно в Firebase и Mixpanel.

  3. Отдельные режимы для отладочной и релизной сборки: Во время разработки в большинстве ситуаций мы хотим отражать события только в консоли. В релизной сборке же следует использовать реальных вендоров аналитики — без изменений логики приложения.

Другие важные вещи, о которых стоит задуматься:

  • Отслеживание переходов между экраннами

  • Переключение между активным и неактивным режимами (например, из соображений конфиденциальности / GDPR)

  • Идентификация пользователя (после входа в систему)

  • Несколько вариантов сборки (dev/staging/prod)

Но сейчас мы сосредоточимся на двух наиболее важных моментах:

  • Изоляция аналитики от логики приложения

  • Поддержка нескольких вендоров аналитики

Давайте рассмотрим две возможные архитектуры и разберем их компромиссы.

Простейшая архитектура аналитики 

Этот сетап был разработан мной на основе предложения, которое я получил в X (Twitter):

import 'dart:developer';

import 'package:flutter/foundation.dart';
import 'package:mixpanel_flutter/mixpanel_flutter.dart';
import 'package:firebase_analytics/firebase_analytics.dart';

class AnalyticsClient {
  const AnalyticsClient(this._analytics, this._mixpanel);
  final FirebaseAnalytics _analytics;
  final Mixpanel _mixpanel;

  Future<void> track(
    String name, {
    Map<String, dynamic> params = const {},
  }) async {
    if (kReleaseMode) {
      await _analytics.logEvent(name: name, parameters: params);
      await _mixpanel.track(name, properties: params);
    } else {
      log('$name $params', name: 'Event');
    }
  }
}

Проще некуда. Это минималистичный общий интерфейс для отслеживания событий:

  • В релизной сборке он отправляет события и в Firebase, и в Mixpanel

  • В режиме отладки они просто выводятся в консоль.

Если вы используете Riverpod, отслеживание событий выглядит следующим образом:

final analytics = ref.read(analyticsClientProvider);
analytics.track('app_created');
analytics.track('app_updated');
analytics.track('app_deleted');
analytics.track('task_completed', parameters: {'count': count});

Если названия ваших событий соответствуют рекомендациям Firebase Analytics (от 1 до 40 буквенно‑цифровых символов с нижними подчеркиваниями), то вам больше не о чем беспокоиться.

Этот подход на удивление гибок, учитывая, как мало кода он требует. Вы можете легко сменить вендоров аналитики, обновив всего лишь один класс, и при этом остальные части вашего приложения останутся нетронутыми.

Но у нее есть недостатки

Которые начинают проявляться по мере роста вашего приложения:

  • Отсутствие условного отслеживания: Вы не можете выборочно отправлять некоторые события в Mixpanel, но не в Firebase. Это может стать проблемой, если вам нужно следить за объемом отправляемых событий (и их стоимостью).

  • Повсюду сплошной хардкод: Имена событий в конечном итоге дублируются по всей кодовой базе. Легко опечатку. Сложно рефакторить. Большие грабли с точки зрения командной работы.

  • Нет единого источника истины: Нет централизованного списка событий или их ожидаемых параметров.

  • Нет автозаполнения: Вы теряете поддержку IDE. Если бы у вас был типобезопасный API, то вы могли бы наслаждаться автозаполнением:

Автозаполнение методов аналитики в VSCode
Автозаполнение методов аналитики в VSCode

Этот сетап может быть достаточно хорош для инди‑разработчиков или небольших приложений, где требуется полный контроль и минимум сложностей.

Однако, если вы работаете с более крупной кодовой базой или в составе команды, то стоит инвестировать во что‑то более серьезное. Давайте рассмотрим более масштабируемую альтернативу.

Архитектура аналитики посложнее 

Если вам нужен типобезопасный API аналитики с автозаполнением и четким разделением ответственности, я рекомендую следующий подход.

Начнем с определения абстрактного интерфейса AnalyticsClient. Для приложения Flutter Ship это может выглядеть следующим образом:

abstract class AnalyticsClient {
  Future<void> trackAppCreated();
  Future<void> trackAppUpdated();
  Future<void> trackAppDeleted();
  Future<void> trackTaskCompleted(int completedCount);
}

Таким образом, все события будут определяться в одном месте, что позволит избежать дублирования и потенциальных ошибок.

Но как нам реализовать поддержку нескольких клиентов?

Вот один из способов структурировать это:

Архитектура аналитики приложения
Архитектура аналитики приложения

Как это работает:

  • Мы определяем все наши события в абстрактном интерфейсе AnalyticsClient.

  • Каждая конкретная реализация (например, LoggerAnalyticsClient, FirebaseAnalyticsClient и т. д.) определяет, как регистрируются эти события.

  • Класс AnalyticsFacade также реализует интерфейс AnalyticsClient, но его задача — просто отправлять события всем зарегистрированным клиентам.

  • Наконец, приложение использует analyticsFacadeProvider для доступа к фасаду и вызова соответствующих методов отслеживания.

Теперь, чтобы отслеживать события из кода вашего приложения, достаточно просто добавить:

final analytics = ref.read(analyticsFacadeProvider);
analytics.trackEditApp();

Поскольку каждое событие представлено отдельным методом, вы также получаете типобезопасность и автозаполнение:

Автозаполнение методов аналитики в VSCode
Автозаполнение методов аналитики в VSCode

Как нам реализовать эту архитектуру?

Пока это всего лишь высокоуровневая архитектура. Чтобы заставить ее работать, нам нужно ответить на три важных вопроса:

  • Как нам зарегистрировать нескольких клиентов в AnalyticsFacade?

  • Как отправлять события каждому клиенту?

  • Как выглядят конкретные подклассы AnalyticsClient?

Далее давайте рассмотрим детали реализации.

Аналитика приложения: детали реализации

Чтобы реализовать архитектуру, о которой мы говорили ранее, нам потребуется несколько ключевых компонентов:

  1. Интерфейс AnalyticsClient, который определяет все события, которые мы хотим отслеживать.

  2. AnalyticsFacade, который реализует AnalyticsClient и делегирует вызовы нескольким клиентам.

  3. LoggerAnalyticsClient для локальной разработки — отправляет события в консоль.

  4. Дополнительные клиенты, такие как FirebaseAnalyticsClient, MixpanelAnalyticsClient и т. д., которые используют SDK от соответствующего вендора.

Для организации всего этого кода я рекомендую разместить его в отдельной папке monitoring:

‣ lib
  ‣ src
    ‣ monitoring
      ‣ analytics_client.dart
      ‣ analytics_facade.dart
      ‣ logger_analytics_client.dart
      ‣ firebase_analytics_client.dart

Вот как работает каждая часть:

1. Интерфейс AnalyticsClient

Это контракт, который описывает все события, поддерживаемые вашим приложением.

abstract class AnalyticsClient {
  // Custom events for the Flutter Ship app.
  // TODO: Replace with your own events.
  Future<void> trackNewAppOnboarding();
  Future<void> trackNewAppHome();
  Future<void> trackAppCreated();
  Future<void> trackAppUpdated();
  Future<void> trackAppDeleted();
  Future<void> trackTaskCompleted(int completedCount);
}

Поскольку этот класс является абстрактным, все методы представляют собой только объявления, без реализации.

2. Класс LoggerAnalyticsClient

Тут все просто: этот класс просто отправляет события в консоль. Он полезен для локальной разработки и тестов, прежде чем мы подключим реальный серверный модуль аналитики.

import 'dart:async';
import 'dart:developer';

import 'package:flutter_ship_app/src/monitoring/analytics_client.dart';

class LoggerAnalyticsClient implements AnalyticsClient {
  const LoggerAnalyticsClient();

  static const _name = 'Event';

  @override
  Future<void> trackNewAppHome() async {
    log('trackNewAppHome', name: _name);
  }

  @override
  Future<void> trackNewAppOnboarding() async {
    log('trackNewAppOnboarding', name: _name);
  }

  @override
  Future<void> trackAppCreated() async {
    log('trackAppCreated', name: _name);
  }

  @override
  Future<void> trackAppUpdated() async {
    log('trackAppUpdated', name: _name);
  }

  @override
  Future<void> trackAppDeleted() async {
    log('trackAppDeleted', name: _name);
  }

  @override
  Future<void> trackTaskCompleted(int completedCount) async {
    log('trackTaskCompleted(completedCount: $completedCount)', name: _name);
  }
}

Мы добавим FirebaseAnalyticsClient позже. Сейчас сосредоточьтесь на том, что вы хотите отслеживать, а не на том, как это будет отправляться.

Существует ли лучший способ определения событий?

Наш текущий подход работает, но он не очень удобен, так как требует много копипаста для добавления новых событий:

// copy-paste when creating new events
@override
Future<void> trackAppCreated() async {
  log('trackAppCreated', name: _name);
}

Следует упомянуть несколько альтернативных вариантов:

  • Перечисления: Определить перечисление (enum) AppEvent, в котором в виде значений будут перечислены все возможные виды событий. Этот вариант не подойдет, если разным событиям нужны разные аргументы.

  • Sealed классы: Использовать базовый sealed класс AppEvent и создавать подкласс каждый раз, когда нам нужно добавить новое событие. Это более гибкий подход, но требует больше кода.

  • Union‑типы freezed: Лаконичный синтаксис и паттернматчинг, но требуется кодогенерации.

Каждый из этих вариантов имеет свои преимущества и недостатки. На данный момент мы будем придерживаться нашего первоначального подхода.

3. Класс AnalyticsFacade

AnalyticsFacade реализует интерфейс AnalyticsClient. Этот класс отвечает за пересылку вызовов методов всем зарегистрированным клиентам.

import 'package:flutter/foundation.dart';
import 'package:flutter_ship_app/src/monitoring/analytics_client.dart';
import 'package:flutter_ship_app/src/monitoring/logger_analytics_client.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'analytics_facade.g.dart';

// https://refactoring.guru/design-patterns/facade
class AnalyticsFacade implements AnalyticsClient {
  const AnalyticsFacade(this.clients);
  final List<AnalyticsClient> clients;

  @override
  Future<void> trackAppOpened() => _dispatch(
        (c) => c.trackAppOpened(),
      );

  @override
  Future<void> trackNewAppHome() => _dispatch(
        (c) => c.trackNewAppHome(),
      );

  @override
  Future<void> trackNewAppOnboarding() => _dispatch(
        (c) => c.trackNewAppOnboarding(),
      );

  @override
  Future<void> trackAppCreated() => _dispatch(
        (c) => c.trackAppCreated(),
      );

  @override
  Future<void> trackAppUpdated() => _dispatch(
        (c) => c.trackAppUpdated(),
      );

  @override
  Future<void> trackAppDeleted() => _dispatch(
        (c) => c.trackAppDeleted(),
      );

  @override
  Future<void> trackTaskCompleted(int completedCount) => _dispatch(
        (c) => c.trackTaskCompleted(completedCount),
      );

  Future<void> _dispatch(
    Future<void> Function(AnalyticsClient client) work,
  ) async {
    for (final client in clients) {
      await work(client);
    }
  }
}

Метод _dispatch уменьшает дублирование кода и транслирует каждое событие всем зарегистрированным клиентам.

Настройка провайдера

Вот как можно отобразить AnalyticsFacade с помощью Riverpod:

@Riverpod(keepAlive: true)
AnalyticsFacade analyticsFacade(Ref ref) {
  return const AnalyticsFacade([
    if (!kReleaseMode) LoggerAnalyticsClient(),
  ]);
}

В отладочных сборках у нас будет логирование в консоль. В релизных сборках мы можем добавлять реальных клиентов, таких как FirebaseAnalyticsClient (подробнее об этом позже).

Этот сетап предоставляет нам понятный, модульный и масштабируемый способ отслеживания событий аналитики — без привязки логики нашего приложения к какому‑либо SDK конкретного вендора.

Какая архитектура лучше?

Архитектура, которую мы только что создали, дает нам:

  • Типобезопасный API отслеживания событий

  • Автозаполнение и поддержка IDE

  • Четкое разделение обязанностей

  • Поддержка нескольких серверов аналитики

Вот визуальное представление:

Архитектура аналитики приложения
Архитектура аналитики приложения

Она гибкая, масштабируемая и готовая к работе в продакшене.

Однако за эту гибкость и масштабируемость приходится платить.

Каждый раз, когда вы добавляете новое событие, вам необходимо:

  • Добавить новый метод в интерфейс AnalyticsClient

  • Реализовать его в AnalyticsFacade

  • Реализовать его для каждого конкретного клиента (например, Logger, Firebase, Mixpanel)

Когда может быть достаточно простого подхода

Для небольших приложений или MVP может быть достаточно более простого подхода:

import 'dart:developer';

import 'package:flutter/foundation.dart';
import 'package:mixpanel_flutter/mixpanel_flutter.dart';
import 'package:firebase_analytics/firebase_analytics.dart';

class AnalyticsClient {
  const AnalyticsClient(this._analytics, this._mixpanel);
  final FirebaseAnalytics _analytics;
  final Mixpanel _mixpanel;

  Future<void> track(
    String name, {
    Map<String, dynamic> params = const {},
  }) async {
    if (kReleaseMode) {
      await _analytics.logEvent(name: name, parameters: params);
      await _mixpanel.track(name, properties: params);
    } else {
      log('$name $params', name: 'Event');
    }
  }
}

Такой подход позволяет быстро реализовать аналитику, но при этом вы теряете важные преимущества:

  • Автозаполнение

  • Типобезопасность

  • Централизованные определения событий

  • Контроль и фильтрация на уровне вендора

Автозаполнение методов аналитики в VSCode
Автозаполнение методов аналитики в VSCode

В итоге: Если вы разрабатываете что‑то более серьезное, чем просто одноразовый прототип, то стоит обратить внимание на более структурированную архитектуру. Хотя она может показаться более сложной в реализации, она окупится лучшей поддерживаемостью, удобством в тестировании и общим опытом разработки.

Давайте теперь посмотрим, как можно использовать analyticsFacadeProvider для отслеживания событий в реальном коде приложения.

Отслеживание пользовательских Событий

Мы определили интерфейс следующим образом:

abstract class AnalyticsClient {
  Future<void> trackNewAppOnboarding();
  Future<void> trackNewAppHome();
  Future<void> trackAppCreated();
  Future<void> trackAppUpdated();
  Future<void> trackAppDeleted();
  Future<void> trackTaskCompleted(int completedCount);
}

Однако, если мы не будем вызывать эти методы, то ничего не будет происходить.

Итак, где нам следует разместить эти вызовы?

  • В виджетах?

  • В контроллерах?

  • Где‑нибудь в другом месте?

Давайте рассмотрим оба варианта.

Отслеживание событий в виджетах и контроллерах

Вот простой пример — кнопка + на главной странице:

Кнопка для добавления нового приложения на главной странице
Кнопка для добавления нового приложения на главной странице

Код отслеживания событий выглядит следующим образом:

IconButton(
  onPressed: () {
    // event tracking code
    unawaited(ref.read(analyticsFacadeProvider).trackNewAppHome());
    // navigation code
    Navigator.of(context).pushNamed(AppRoutes.createApp);
  },
  icon: Icon(Icons.add),
)

В этом случае вызов аналитики помещается непосредственно в коллбек onPressed.

? Функция unawaited используется здесь для отправки события без блокирования выполнения. Это fire‑and‑forget вызовы — вам не нужно ждать их завершения. Подробнее об этом можно почитать здесь: Используйте unawaited для своих вызовов аналитики .

Однако, если ваша логика более сложная, рекомендуется перенести вызовы аналитики в контроллер.

Вот пример пользовательского класса контроллера:

/// This class holds the business logic for creating, editing, and deleting apps
/// using the underlying AppDatabase class for data persistence.
/// More info here: https://codewithandrea.com/articles/flutter-presentation-layer/
@riverpod
class CreateEditAppController extends _$CreateEditAppController {
  @override
  void build() {
    // no-op
  }

  Future<void> createOrEditApp(App? existingApp, String newName) async {
    final db = ref.read(appDatabaseProvider);
    // * Update the DB
    if (existingApp != null) {
      await db.editAppName(appId: existingApp.id, newName: newName);
    } else {
      await db.createNewApp(name: newName);
    }
    // * Analytics code
    if (existingApp != null) {
      unawaited(ref.read(analyticsFacadeProvider).trackAppUpdated());
    } else {
      unawaited(ref.read(analyticsFacadeProvider).trackAppCreated());
    }
  }

  Future<void> deleteAppById(int appId) async {
    await ref.read(appDatabaseProvider).deleteAppById(appId);
    ref.read(analyticsFacadeProvider).trackAppDeleted();
  }
}

Этот контроллер отвечает за все изменения и взаимодействия с базой данных, что делает его идеальным местом для отслеживания связанных событий аналитики.

Рекомендации по отслеживанию событий

  • Никогда не добавляйте отслеживание событий в build(). Эта функция может вызываться десятки раз в секунду во время анимаций. То же самое касается initState() и других методов жизненного цикла.

  • Добавляйте отслеживание в колбеки виджетов типа onPressed. Если логика сложная, перенесите ее в контроллер или служебный класс.

  • Идеальным вариантом для отслеживания событий являются контроллеры. В них нет логики пользовательского интерфейса, их проще тестировать, и в них уже заложена логика, которая инициирует события.

  • Не отслеживайте события на уровне данных или сетей. События должны отслеживаться в источнике (уровень пользовательского интерфейса) для большей точности.

  • Используйте unawaited() для всех вызовов аналитики. Это позволит вам не блокировать пользовательский интерфейс и не заботиться о результатах — просто запустите и забудьте.

Интеграция Firebase

До сих пор мы использовали LoggerAnalyticsClient для вывода событий в консоль.

Однако для производственных приложений вам потребуется интеграция с реальной серверной аналитикой, такой как Firebase Analytics, Mixpanel или PostHog.

Благодаря нашему AnalyticsFacade мы можем легко справиться с этой задачей, не меняя код нашего приложения. Мы просто добавим новый клиент, зарегистрируем его, и все готово. В этом и заключается сила разделения ответственности.

Давайте рассмотрим процесс интеграции Firebase Analytics.

Добавление Firebase в наше Flutter-приложение 

Официальное руководство очень хорошо объясняет процесс добавления Firebase в наше Flutter‑приложение.

Если ваше приложение поддерживает несколько вариантов сборок (например, dev, staging, prod), настройка может потребовать от вас дополнительных шагов. Я уже рассказывал об этом в другой статье:

Создание класса FirebaseAnalyticsClient

Это достаточно простая реализация интерфейса AnalyticsClient с использованием Firebase SDK:

import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:flutter_ship_app/src/monitoring/analytics_client.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'firebase_analytics_client.g.dart';

class FirebaseAnalyticsClient implements AnalyticsClient {
  const FirebaseAnalyticsClient(this._analytics);
  final FirebaseAnalytics _analytics;

  @override
  Future<void> trackNewAppHome() async {
    await _analytics.logEvent(name: 'new_app_home');
  }

  @override
  Future<void> trackNewAppOnboarding() async {
    await _analytics.logEvent(name: 'new_app_onboarding');
  }

  @override
  Future<void> trackAppCreated() async {
    await _analytics.logEvent(name: 'app_created');
  }

  @override
  Future<void> trackAppUpdated() async {
    await _analytics.logEvent(name: 'app_updated');
  }

  @override
  Future<void> trackAppDeleted() async {
    await _analytics.logEvent(name: 'app_deleted');
  }

  @override
  Future<void> trackTaskCompleted(int completedCount) async {
    await _analytics.logEvent(
      name: 'task_completed',
      parameters: {'count': completedCount},
    );
  }
}

@Riverpod(keepAlive: true)
FirebaseAnalyticsClient firebaseAnalyticsClient(Ref ref) {
  return FirebaseAnalyticsClient(FirebaseAnalytics.instance);
}

Регистрация клиента в фасаде

Чтобы добавить клиент Firebase, обновите свой analyticsFacadeProvider следующим образом:

@Riverpod(keepAlive: true)
AnalyticsFacade analyticsFacade(Ref ref) {
  final firebaseAnalyticsClient = ref.watch(firebaseAnalyticsClientProvider);
  return AnalyticsFacade([
    firebaseAnalyticsClient,
    if (!kReleaseMode) const LoggerAnalyticsClient(),
  ]);
}

И вот так, без каких‑либо изменений в остальной части вашего приложения, ваши события теперь отправляются в Firebase Analytics.

Вы можете увидеть, как они отображаются в консоли Firebase:

Первый активный пользователь, отображающийся в дашборде Firebase Analytics
Первый активный пользователь, отображающийся в дашборде Firebase Analytics

Вот как это выглядит в моем приложении Flutter Tips:

Дашборд Firebase Analytics для приложения Flutter Tips
Дашборд Firebase Analytics для приложения Flutter Tips

Аналитика Flutter-приложений: Заключение

В этой статье мы обсудили все, что необходимо знать для реализации базовой аналитики производственного уровня в вашем Flutter‑приложении:

  • Почему аналитика необходима для уверенных релизов и роста

  • Как продумать и правильно выбрать события для отслеживания

  • Простая архитектура для быстрой реализации аналитики

  • Масштабируемая типобезопасная архитектура для более крупных приложений и команд

  • Как отслеживать события из виджетов и контроллеров

  • Как интегрировать Firebase Analytics, не влияя на остальную логику приложения

Однако это лишь первый шаг к реализации аналитики в реальных приложениях.

Прежде чем публиковать ваше приложение, стоит учесть несколько важных моментов:

  • Отслеживание экранного вида: Используйте навигационный наблюдатель, чтобы автоматически фиксировать переходы между экранами.

  • Аналитика в разных режимах: Позвольте пользователям отключать отслеживание, если это необходимо (например, для соблюдения GDPR).

  • Идентификация пользователя: Привязывайте события к зарегистрированным пользователям, чтобы отслеживать их на разных устройствах и анализировать воронку конверсии.

  • Интеграция с Mixpanel или PostHog: Firebase является бесплатным решением, но такие инструменты, как Mixpanel, предлагают мощную фильтрацию и отчетность и гораздо более удобны для обеспечения конфиденциальности.


Хотите узнать, как встроить аналитику в  Flutter‑приложение так, чтобы каждый экран и клик превращались в ценные продуктовые инсайты? Тогда приглашаем всех желающих на бесплатный открытый урок, который пройдет 20 мая в 20:00.

Немного практики в тему — попробуйте пройти вступительный тест по Flutter и получите обратную связь по своим знаниям.

Комментарии (0)