Представьте себе ситуацию: вы рассказали всем своим родным и близким о своем замечательном новом приложении. Возможно, вам даже посчастливилось сформировать список ожидания из заинтересованных пользователей. Вы сгораете от нетерпения нажать кнопку «Publish».
Но погодите минутку…
А как вы узнаете, что действительно работает, а что нуждается в улучшении?
Сколько пользователей завершили онбординг?
Сколько пользователей зарегистрировались и создали учетную запись?
Сколько пользователей достигли пейвола и сколько конвертировались?
Без аналитики вы действуете вслепую.
Вы принимаете решения о продукте, основываясь на предположениях, а не на данных. Это самый простой способ замедлить ваш рост, потратить впустую время разработки или, что еще хуже, потерять пользователей.
Важность аналитики
Аналитика предоставляет вам данные, необходимые для достижения успеха. Она помогает вам:
Измерять вовлеченность: Кто использует ваше приложение, как часто и как долго?
Понимать ретеншн: Где пользователи отваливаются и почему?
Отслеживать использование функций: Что популярно? Что игнорируется?
Оптимизировать прибыль: Отслеживание покупок, подписок и оттока.
Знать своих пользователей: Демография, устройства, платформы.
Контролировать пользовательский опыт: Составление карты пользовательского опыта и выявление проблемных областей.
Получив эту информацию вы перестанете гадать и начнете принимать обоснованные решения о продукте, которые действительно улучшат ваше приложение.

Чему вы научитесь
В этой статье я покажу вам, как эффективно отслеживать аналитику в вашем Flutter‑приложении — от базового логирования событий до масштабируемой архитектуры, которая будет работать с разными системами, такими как Firebase и Mixpanel.
Ключевые моменты, которые мы рассмотрим:
Что стоит отслеживать: Выбор ключевых событий и их значимость.
Как структурировать аналитику: Простые и масштабируемые архитектуры для отслеживания событий.
Настройка Firebase Analytics: Как настроить аналитику в реальном приложении.
Что ж, давайте приступим.
Эта статья познакомит вас с основами. Если вы хотите углубить свои знания, я рекомендую ознакомиться с курсом "Flutter Mobile Developer".
Введение в отслеживание событий
Если вы спросите пять разных разработчиков, как они внедрили аналитику, вы, вероятно, получите пять разных ответов.
Как это часто бывает в программной инженерии, не существует универсального решения — только компромиссы.
Поэтому, прежде чем мы углубимся в код, давайте немного отдалимся и ответим на несколько ключевых вопросов:
Какие события нам следует отслеживать?
Как мы должны их отслеживать?
Каким требованиям должно соответствовать наше решение?
Как только мы разберемся с этим, мы сможем выбрать подходящую архитектуру.
Какие события нам нужно отслеживать?
Говоря простым языком, события представляют собой взаимодействия между пользователем и вашим приложением.
Давайте рассмотрим в качестве примера мое приложение Flutter Ship:
Демо‑версия приложения Flutter Ship
Приложение поможет вам «поставить все галочки» перед выпуском приложений Flutter. По сути, это предварительно заполненный список дел.
Задача этого приложения заключается в том, чтобы помочь вам выполнить все необходимые шаги перед релизом ваших Flutter‑приложений. По сути, это просто готовый список задач: https://bizz84.github.io/flutter_ship_app_web/#apps [Откройте в отдельном окне]
Если бы вам нужно было добавить аналитику в это приложение, какие события вы бы отслеживали?
Важно сосредоточиться на событиях, которые:
Помогают пользователю добиться успеха (например, выполнить задачи)
Способствуют успеху вашего бизнеса (например, регистрация пользователей, ретеншн, монетизация)
Для этого приложения значимыми событиями являются:
Создание нового приложения
Внесение изменений в существующее приложение
Удаление приложения
Выполнение задачи
Остальные события не являются обязательными для отслеживания. Не стоит отслеживать все подряд только потому что вы можете это сделать.
Отслеживание событий с помощью 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
Вы не можете повторно использовать логику или отправлять события сразу нескольким вендорам
Вы смешиваете бизнес‑логику с деталями реализации
Мы можем добиться гораздо большего, если определим четкий интерфейс, который отделит отслеживание событий от всего остального.
Но для этого нам потребуется сформировать список требований.
Аналитика приложения: Требования
Прежде чем приступить к реализации, давайте четко сформулируем требования к аналитической системе.
Ниже представлен ряд ключевых требований, которые нам необходимо учесть:
Разделение ответственности: Код приложения не должен напрямую взаимодействовать с SDK‑пакетами вендоров (например, Firebase или Mixpanel). Вместо этого все отслеживание событий будет происходить через простой и понятный интерфейс.
Поддержка нескольких клиентов: Нам необходимо иметь возможность отправлять события сразу нескольким вендорам. Например, мы можем захотеть реализовать логирование одновременно в Firebase и Mixpanel.
Отдельные режимы для отладочной и релизной сборки: Во время разработки в большинстве ситуаций мы хотим отражать события только в консоли. В релизной сборке же следует использовать реальных вендоров аналитики — без изменений логики приложения.
Другие важные вещи, о которых стоит задуматься:
Отслеживание переходов между экраннами
Переключение между активным и неактивным режимами (например, из соображений конфиденциальности / 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, то вы могли бы наслаждаться автозаполнением:

Этот сетап может быть достаточно хорош для инди‑разработчиков или небольших приложений, где требуется полный контроль и минимум сложностей.
Однако, если вы работаете с более крупной кодовой базой или в составе команды, то стоит инвестировать во что‑то более серьезное. Давайте рассмотрим более масштабируемую альтернативу.
Архитектура аналитики посложнее
Если вам нужен типобезопасный 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();
Поскольку каждое событие представлено отдельным методом, вы также получаете типобезопасность и автозаполнение:

Как нам реализовать эту архитектуру?
Пока это всего лишь высокоуровневая архитектура. Чтобы заставить ее работать, нам нужно ответить на три важных вопроса:
Как нам зарегистрировать нескольких клиентов в
AnalyticsFacade?Как отправлять события каждому клиенту?
Как выглядят конкретные подклассы
AnalyticsClient?
Далее давайте рассмотрим детали реализации.
Аналитика приложения: детали реализации
Чтобы реализовать архитектуру, о которой мы говорили ранее, нам потребуется несколько ключевых компонентов:
Интерфейс
AnalyticsClient, который определяет все события, которые мы хотим отслеживать.AnalyticsFacade, который реализуетAnalyticsClientи делегирует вызовы нескольким клиентам.LoggerAnalyticsClientдля локальной разработки — отправляет события в консоль.Дополнительные клиенты, такие как
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');
    }
  }
}
Такой подход позволяет быстро реализовать аналитику, но при этом вы теряете важные преимущества:
Автозаполнение
Типобезопасность
Централизованные определения событий
Контроль и фильтрация на уровне вендора

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

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

Аналитика Flutter-приложений: Заключение
В этой статье мы обсудили все, что необходимо знать для реализации базовой аналитики производственного уровня в вашем Flutter‑приложении:
Почему аналитика необходима для уверенных релизов и роста
Как продумать и правильно выбрать события для отслеживания
Простая архитектура для быстрой реализации аналитики
Масштабируемая типобезопасная архитектура для более крупных приложений и команд
Как отслеживать события из виджетов и контроллеров
Как интегрировать Firebase Analytics, не влияя на остальную логику приложения
Однако это лишь первый шаг к реализации аналитики в реальных приложениях.
Прежде чем публиковать ваше приложение, стоит учесть несколько важных моментов:
Отслеживание экранного вида: Используйте навигационный наблюдатель, чтобы автоматически фиксировать переходы между экранами.
Аналитика в разных режимах: Позвольте пользователям отключать отслеживание, если это необходимо (например, для соблюдения GDPR).
Идентификация пользователя: Привязывайте события к зарегистрированным пользователям, чтобы отслеживать их на разных устройствах и анализировать воронку конверсии.
Интеграция с Mixpanel или PostHog: Firebase является бесплатным решением, но такие инструменты, как Mixpanel, предлагают мощную фильтрацию и отчетность и гораздо более удобны для обеспечения конфиденциальности.
Хотите узнать, как встроить аналитику в Flutter‑приложение так, чтобы каждый экран и клик превращались в ценные продуктовые инсайты? Тогда приглашаем всех желающих на бесплатный открытый урок, который пройдет 20 мая в 20:00.
Немного практики в тему — попробуйте пройти вступительный тест по Flutter и получите обратную связь по своим знаниям.
          
 
gidrokolbaska
Метод _dispatch можно улучшить вот так:
Зачем вызывать последовательно каждого «клиента», если можно распараллелить?