4.6. Firebase: зачем нужен во Flutter и как подключить

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

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

В этом параграфе мы кратко рассмотрим, что входит в состав Firebase, а также покажем на примерах, как подключить и использовать основные инструменты:

  • Firebase Crashlytics

  • Firebase Analytics

  • Firebase Authentication

  • Firebase Firestore

  • Firebase Remote Config

  • Firebase Hosting

Но сначала коротко рассмотрим, что вообще входит в Firebase.

Продукты Firebase

Сервисы Firebase можно разделить на разные группы:

  • для разработки;

  • подготовки к релизу;

  • продуктовой и технической аналитики.

Вот что входит в каждую из них.

Для разработки

К этой категории относятся сервисы, которые полезны на этапе разработки вашего продукта, например:

Для подготовки к релизу

Пригодятся на более поздних этапах проекта, перед релизом:

Для продуктовой и технической аналитики

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

Но прежде чем добавлять эти инструменты во Flutter-проект, давайте разберёмся с настройкой окружения.

Подготовка окружения

Для того чтобы интегрировать Firebase во Flutter-приложение, нужно выполнить некоторые шаги, описанные ниже. Если возникнут трудности, можно посмотреть, какие шаги предлагает официальная документация.

Шаг 1. Установка необходимых инструментов командной строки

  1. Установка Firebase CLI:
1curl -sL <https://firebase.tools> | bash
  1. Войдите в Firebase, используя свою учётную запись Google и выполнив следующую команду:
1firebase login

Откроется браузер для авторизации со своего Google-аккаунта.

  1. Установите интерфейс командной строки FlutterFire глобально, эта утилита позволит интегрировать Firebase в наше приложение. Выполните следующую команду из любого каталога:
1dart pub global activate flutterfire_cli

Шаг 2. Настройка приложения для использования Firebase

  1. Создадим Flutter-проект:
1flutter create example
  1. Чтобы установить основной плагин, в каталоге проекта Flutter выполните следующую команду:
1flutter pub add firebase_core
  1. Чтобы подключить ваш Flutter-проект к Firebase-проекту, в каталоге проекта Flutter выполните следующую команду:
1flutterfire configure

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

Если вы уже создали проект в сервисе Firebase до этого пункта, просто выберите его в списке после выполнения команды. А если создаёте впервые, можно выбрать пункт create a new project в списке прямо из терминала.

Создание проекта в админке Console Firebase или через терминал очень понятно, не будем на этом останавливаться, а перейдём сразу к настройке запуска приложения:

  1. В файл lib/main.dart импортируйте основной плагин Firebase и файл конфигурации, созданный ранее:

    1import 'package:firebase_core/firebase_core.dart';
    2import 'firebase_options.dart';
    
  2. Также в файле main.dart инициализируйте Firebase, используя объект DefaultFirebaseOptions, экспортированный файлом конфигурации, который будет доступен из пакета firebase_core:

    1Future<void> main() async {
    2  WidgetsFlutterBinding.ensureInitialized();
    3
    4  await Firebase.initializeApp(
    5    options: DefaultFirebaseOptions.currentPlatform,
    6  );
    7
    8  runApp(MyApp());
    9}
    

    Так как метод Firebase.initializeApp() использует вызов нативного метода, прежде чем запустить приложение, нам нужно воспользоваться WidgetsFlutterBinding.ensureInitialized(), для того чтобы дождаться инициализацию привязки Flutter Engine с фреймворком.

Это основная настройка Flutter-проекта для работы с Firebase. Приступим к установке первого продукта.

Firebase Crashlytics

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

Существует два типа ошибок — crash и non-fatal.

  • Crash — это фатальная ошибка, которая приведёт к принудительному закрытию приложения.

  • Non-fatal — это исключение или нефатальная ошибка, которые поймало приложение, а не обработчик.

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

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

Важно

Важно

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

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

Давайте подключим Firebase Crashlytics к нашему приложению.

Устанавливаем необходимые зависимости

Хоть мы и установили firebase_core, все сервисы Firebase живут отдельными пакетами. Поэтому нам надо выполнить команду для установки пакета firebase_crashlytics.

1flutter pub add firebase_crashlytics

Настроим обработчик сбоев

Чтобы автоматически отслеживать все фатальные ошибки, возникающие во Flutter, можно переопределить FlutterError.onError и передавать их в сервис Crashlytics с помощью FirebaseCrashlytics.instance.recordFlutterFatalError:

1Future<void> main() async {
2  WidgetsFlutterBinding.ensureInitialized();
3
4  await Firebase.initializeApp(
5    options: DefaultFirebaseOptions.currentPlatform,
6  );
7
8  FlutterError.onError = (error) => FirebaseCrashlytics.instance.recordFlutterFatalError(error);
9
10  runApp(MyApp());
11}

Чтобы автоматически отслеживать и фатальные, и нефатальные ошибки, возникающие в синхронных операциях Flutter, можно переопределить обработчик с помощью FirebaseCrashlytics.instance.recordFlutterError:

1Future<void> main() async {
2  WidgetsFlutterBinding.ensureInitialized();
3
4  await Firebase.initializeApp(
5    options: DefaultFirebaseOptions.currentPlatform,
6  );
7
8  FlutterError.onError = (error) => FirebaseCrashlytics.instance.recordFlutterError(error);
9
10  runApp(MyApp());
11}

Чтобы перехватывать асинхронные ошибки, которые не обрабатываются фреймворком Flutter, используйте PlatformDispatcher.instance.onError. Это позволит отслеживать ошибки, возникающие в асинхронных операциях, и при необходимости отправлять их в Crashlytics для дальнейшего анализа:

1Future<void> main() async {
2  WidgetsFlutterBinding.ensureInitialized();
3
4  await Firebase.initializeApp(
5    options: DefaultFirebaseOptions.currentPlatform,
6  );
7
8  FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;
9
10  PlatformDispatcher.instance.onError = (error, stack) {
11    FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
12    return true;
13  };
14
15  runApp(MyApp());
16}

Также вам может понадобиться передавать дополнительную информацию через Crashlytics для быстрой диагностики проблемы. Например, какое-то новое поведение в приложении, которое включили с помощью Remote Config (о нём поговорим ниже).

Для этого можем воспользоваться следующими полями:

1await FirebaseCrashlytics.instance.recordError(
2  error,
3  stackTrace,
4  reason: 'Non-fatal',
5  information: [
6    'Гость', 
7    'Версия приложения 1.0.0',
8    'Включён эксперимент с новым экраном'
9  ],
10);

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

Создадим тестовое исключение

Чтобы завершить настройку Crashlytics и просмотреть отловленные нефатальные ошибки на панели мониторинга Crashlytics Firebase, вам нужно принудительно создать тестовое исключение.

Давайте по нажатии на кнопку будем кидать необработанное исключение:

1TextButton(
2  child: const Text('Исключение'),
3  onPressed: () => throw Exception(),
4),

И на всякий случай проверим обработку исключений в асинхронных операциях:

1Column(
2  mainAxisAlignment: MainAxisAlignment.center,
3  children: [
4    TextButton(
5      child: const Text('Исключение'),
6      onPressed: () => throw Exception(),
7    ),
8    TextButton(
9      child: const Text('Асинхронное исключение'),
10      onPressed: () async => throw Exception(),
11    ),
12  ],
13),

Найдём нефатальную ошибку в консоли Firebase

Запустим приложение и нажмём кнопки TextButton.

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

Откройте Crashlytics своего проекта, и если вы сделали всё правильно, вы должны увидеть две необработанные ошибки, которые отправили при нажатии на эти кнопки.

Важно

Важно

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

Firebase Analytics

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

Например, если в Firebase Crashlytics появилась трудновоспроизводимая ошибка, можно отправить в Firebase Analytics дополнительные данные о том, как пользователь двигался по приложению, и восстановить последовательность действий, которая привела к сбою. Даже для показа графиков и отчётов в Firebase Crashlytics необходимо подключить аналитику.

Также сервис полезен для продуктовой аналитики.

Как же?

Допустим, вы видите, что из 100 новых пользователей к 30‑му дню остаются только 10. Анализ метрик показывает: 60 человек не проходят онбординг и уходят.

Вы (или ваш продакт) видите, что тут проблемное место. Дальше дело техники: понять, где именно спотыкаются пользователи, провести редизайн и сделать новые замеры.

Возможности

Вот что можно делать с помощью Firebase Analytics:

  • Отправлять кастомные события.

  • Замерять время, проведённое в разделе.

  • Строить воронки и считать конверсию.

  • Использовать результаты для подведения итогов экспериментов.

  • Сегментировать аудиторию по событиям или активности в разделах.

Установка и подключение Firebase Analytics в проект

Так как ранее мы уже настроили Firebase, нам осталось только подключить пакет для аналитики.

Установим зависимость

Из корня вашего проекта Flutter выполните следующую команду, чтобы установить плагин:

1flutter pub add firebase_analytics

Отправим аналитику

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

Чтобы завершить настройку Firebase Analytics и просмотреть исходные данные на панели аналитики консоли Firebase, вам надо запустить приложение и отправить какое-нибудь тестовое событие.

Давайте по нажатии на кнопку будем отправлять тестовое событие под названием button/click с некоторыми параметрами:

1TextButton(
2  child: const Text('Аналитика'),
3  onPressed: () => FirebaseAnalytics.instance.logEvent(
4      name: 'button/click',
5      parameters: {
6        'page' : 'main',
7      },
8    ),
9),

Запустим проект и нажмём кнопку.

Проверим аналитику

Мы отправили событие button/click с параметром страницы, на которой находимся. Зайдите в Console Firebase, откройте раздел Analytics -> Events и найдите наше событие в списке.

Немного про события

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

Аналитика автоматически регистрирует для вас некоторые события. Вам не нужно добавлять какой-либо код для их получения. Но если вам нужно собирать дополнительные данные, то вы можете зарегистрировать в своём приложении до 500 типов событий Analytics. Общий объём событий, регистрируемых вашим приложением, не ограничен.

Важно

Важно

Имена событий чувствительны к регистру. Регистрация двух событий, имена которых различаются только по регистру, приведёт к появлению двух разных событий.

Firebase Authentication

Это удобный инструмент от Firebase для управления авторизацией пользователей в приложениях.

Он поддерживает веб-, мобильные и игровые платформы, предоставляя простые API и гибкие методы аутентификации: через электронную почту и пароль c Magic Link, номер телефона, анонимный вход, социальные сети и OAuth 2.0.

Сервис обеспечивает надёжную защиту данных, включая многоуровневую проверку подлинности, что позволит разработчику не тратить время на создание своего сервиса авторизации.

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

Приступим к установке и настройке.

Добавим зависимости в проект

Установим две зависимости:

  • firebase_auth — это основной пакет, который предоставляет инструменты для авторизации через сервис Firebase Authentication;

  • firebase_ui_auth — это вспомогательный пакет, который предоставляет UI-компоненты для него. Форма для входа по электронной почте и паролю, ввод кода из СМС и кнопки авторизации через социальные сети.

1flutter pub add firebase_auth firebase_ui_auth 

Настроим сервис

Прежде чем создавать и использовать авторизацию, её нужно настроить в консоли Firebase:

  1. Откройте Console Firebase, зайдите в ваш проект и перейдите в раздел Authentication.

  2. Перед тем как начать использовать, инструмент нужно включить, если он ещё не активен.

  3. Для Firebase Authentication используются паттерны авторизации, или, как они названы в коде, провайдеры. Выберите и активируйте нужные провайдеры входа. В нашем случае это Email/Password и Anonymous — вход под ролью «Гость», то есть без авторизации.

Создадим вспомогательный класс

Для авторизации давайте сделаем возможность выполнять вход под гостевым пользователем и выходить из аккаунта в отдельном классе:

1class UserRepository {
2  final FirebaseAuth _firebaseAuth;
3
4  UserRepository(this._firebaseAuth);
5
6  bool get isAuthorized => _firebaseAuth.currentUser != null;
7
8  User? get user => _firebaseAuth.currentUser;
9
10  Future<void> singInAnonymously() async {
11    await _firebaseAuth.singInAnonymously();
12  }
13
14  Future<void> singOut() async {
15    await _firebaseAuth.singOut();
16  }
17}

UI авторизации

Создадим простой экран авторизации для приложения:

1class AuthPage extends StatelessWidget {
2  final UserRepository _userRepository;
3  const AuthPage(this._userRepository, {super.key});
4
5  @override
6  Widget build(BuildContext context) {
7    return SignInScreen(
8      providers: [
9        EmailAuthProvider(),
10      ],
11      styles: const {
12        EmailFormStyle(),
13      },
14      footerBuilder: (context, authAction) => OutlinedButton(
15        onPressed: _userRepository.signInAnonymously,
16        child: const Text('Зайти как Гость'),
17      ),
18      actions: [
19        AuthStateChangeAction<SignedIn>((context, state) {}),
20      ],
21    );
22  }
23}

Посмотрим на параметры, которые мы использовали в классе SignInScreen:

  • providers — в этот параметр вносим тот паттерн входа, который выбрали в качестве основного из консоли Firebase. В нашем случае это вход по электронной почте.

  • styles — параметр говорит сам за себя: можно добавить некоторые стили к этому экрану.

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

  • actions — действие на состояние авторизации, при изменении которого можем перейти на другой экран, например:

1///
2actions: [
3  AuthStateChangeAction<SignedIn>((context, state) {
4      if (state.user != null) {
5  // Пользователь есть, переходим на главный экран
6        _navigateToHomePage();
7      } else {
8  // Пользователя нет, показываем ошибку
9        _showErrorDialog();
10      }
11    },
12  ),
13],
14///
15

В целом использование Firebase Authentication в проекте Flutter позволяет разработчикам сосредоточиться на создании функциональности своего приложения, не тратя времени на реализацию сложных механизмов аутентификации. Но с увеличением аудитории этот сервис становится платным. Подробнее можно посмотреть на официальном сайте.

Firebase Firestore

Это облачная NoSQL-база данных, инструмент для хранения и синхронизации данных в реальном времени.

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

Этот инструмент не только заботится о походах в сеть к данным из Firebase, но и сохраняет их локально на мобильном устройстве. Политику сохранения данных можно настраивать. Например, выставлять время жизни или вообще не сохранять.

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

Подключение пакета

Выполним команду:

1flutter pub add cloud_firestore

Настройка Firebase

В Console Firebase добавим Firestore в список используемых продуктов и нажмём кнопку Create database.

Интерфейс предложит создать базу данных в одном из двух режимов:

  • продакшн-режим (production mode) — режим полной конфиденциальности, вы не сможете читать и записывать данные из консоли Firebase;

  • тестовый режим (test mode) — ваши данные будут открыты по умолчанию всегда, чтобы быстро настроить работу.

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

База данных готова. Теперь создадим коллекцию tasks, сгенерируем ID документа и добавим следующие поля со значением:

Атрибут

Тип данных

title

String

is_done

Boolean

Сохраняем — и наша таблица готова! Вернёмся обратно к проекту.

Подготовка проекта для получения и использования данных

Для начала создадим модель по образу созданной в базе данных Firestore с помощью пакета freezed:

1import 'package:freezed_annotation/freezed_annotation.dart';
2
3part 'task.freezed.dart';
4part 'task.g.dart';
5
6@freezed
7class Task with _$Task {
8  const factory Task({
9    String? id,
10    @JsonKey(name: 'title') required String title,
11    @JsonKey(name: 'is_done') required bool isDone,
12  }) = _Task;
13
14  factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
15}
16

Создадим вспомогательный репозиторий для работы с этими данными:

1
2class TaskRepository {
3  final FirebaseFirestore _firestore;
4
5  TaskRepository(this._firestore);
6
7  static const String dbName = 'tasks';
8
9  late final _tasksCollectionRef = _firestore.collection(dbName).withConverter(
10        fromFirestore: (snapshot, _) => Task.fromJson(
11          snapshot.data()!,
12        ).copyWith(
13          id: snapshot.id,
14        ),
15        toFirestore: (task, _) => task.toJson(),
16      );
17
18  // Стрим со всеми задачами
19  Stream<List<Task>> get tasksStream => _tasksCollectionRef
20      .snapshots()
21      .map((snapshot) => snapshot.docs.map((doc) => doc.data()).toList());
22
23  // Добавление новой задачи
24  Future<void> add(Task task) async {
25    await _tasksCollectionRef.add(task);
26  }
27
28  // Обновление задачи
29  Future<void> update(Task task) async {
30    await _tasksCollectionRef.doc(task.id).update(task.toJson());
31  }
32
33  // Очистка БД
34  Future<void> clearDatabase() async {
35    final tasksSnapshot = await _tasksCollectionRef.get();
36    await _firestore.runTransaction((transaction) async {
37      for (final doc in tasksSnapshot.docs) {
38        transaction.delete(doc.reference);
39      }
40    });
41  }
42}
43

И можем использовать этот репозиторий для получения, добавления и обновления данных из Firebase Firestore.

Напишем простой экран для использования этого репозитория, в котором будет подписка на стрим с нашими значениями из таблицы tasks. Если данные не пришли, показываем анимацию загрузки. Если есть задачи или пришла пустая коллекция — показываем поле ввода и список Task.

1class TasksPage extends StatefulWidget {
2  const TasksPage({super.key});
3
4  @override
5  State<TasksPage> createState() => _TasksPageState();
6}
7
8class _TasksPageState extends State<TasksPage> {
9  late TaskRepository taskRepository;
10
11  @override
12  void initState() {
13    taskRepository = TaskRepository(FirebaseFirestore.instance);
14    super.initState();
15  }
16
17  @override
18  Widget build(BuildContext context) {
19    return Scaffold(
20      appBar: AppBar(
21        title: Text('Мои задачи'),
22      ),
23      body: StreamBuilder<List<Task>>(
24        stream: taskRepository.tasksStream,
25        builder: (context, snapshot) {
26          final data = snapshot.data;
27
28          if (data == null) {
29            return Center(
30              child: CircularProgressIndicator(),
31            );
32          }
33
34          return ListView.separated(
35            itemCount: data.length + 1,
36            itemBuilder: (context, index) {
37
38              if (index == data.length) {
39                return TextField(
40                  onSubmitted: (value) => taskRepository.add(
41                    Task(title: value, isDone: false),
42                  ),
43                );
44              }
45
46              final task = data[index];
47              return ListTile(
48                title: Text(task.title),
49                trailing: Checkbox.adaptive(
50                  value: task.isDone,
51                  onChanged: (value) => taskRepository.update(
52                    task.copyWith(isDone: value ?? false),
53                  ),
54                ),
55              );
56            },
57            separatorBuilder: (_, __) => Divider(),
58          );
59        },
60      ),
61    );
62  }
63}

Использование

Запустите проект. Если вы всё сделали правильно, то увидите самую первую задачу, которую добавили из консоли Firebase при создании таблицы. При нажатии чекбокс изменит состояние, а если мы введём в поле текст и отправим данные, то создастся ещё одна задача.

Правила безопасности в Firebase Firestore

Firebase Firestore Security Rules — это механизм, позволяющий контролировать доступ к данным в базе данных Firestore на основе условий, заданных разработчиком.

Эти правила позволяют управлять тем, кто может читать или изменять данные, исходя из параметров, таких как аутентификация пользователя или содержимое документа. Firestore Security Rules работают в реальном времени, применяя ограничения при каждом запросе.

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

Откройте консоль Firebase и перейдите в настройки Cloud Firestore, где управляются правила доступа. Там создайте следующее условие:

1rules_version = '2';
2
3service cloud.firestore {
4  match /databases/{database}/documents {
5
6    // Начало
7    match /tasks/{tasksid} {
8      allow read;
9      allow write: if resource.data.is_done == false;
10    }
11    // Конец
12  }
13}

Мы добавили правило для таблицы tasks: читать её может каждый (allow read;), а изменять задачи из таблицы tasks можно только те, которые ещё не выполнены, — allow write: if resource.data.is_done == false;.

Сохраним и запустим приложение. Если всё сделано верно, вы увидите, что задачу удаётся отметить как выполненную, а снять флаг уже не получится, значит, правила работают.

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

Firebase Remote Config

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

Подключение пакета

В директории проекта выполним команду:

1flutter pub add firebase_remote_config

Также для работы сервиса Remote Config необходимо добавить Firebase Analytics, если вы ещё этого не сделали:

1flutter pub add firebase_analytics

В документации есть примечание: если вы используете Remote Config в macOS, включите общий доступ к цепочке ключей в Xcode.

Добавим конфиги в консоли Firebase

Перейдём в консоль Firebase и зайдём в сервис Remote Config. Вас могу попросить подключить Google Analytics для работы Remote Config. Далее зайдём в админку, где находятся все созданные конфиги. Для начала создадим два поля, например:

Ключ

Тип данных

Описание

title_for_main_page

String

Заголовок для главной страницы

enable_second_button

Boolean

Будет включать или выключить вторую кнопку

Создадим вспомогательный репозиторий

Давайте вынесем всё, что связано с FirebaseRemoteConfig, в отдельный класс:

1class ConfigRepository {
2  static void init() {
3    FirebaseRemoteConfig.instance
4      ..setConfigSettings(
5        RemoteConfigSettings(
6          fetchTimeout: const Duration(seconds: 5),
7          minimumFetchInterval: const Duration(seconds: 10),
8        ),
9      )
10      ..setDefaults({
11        'title_for_main_page': 'default title',
12        'enable_second_button': false,
13      });
14  }
15
16  Stream<RemoteConfigUpdate> get onConfigUpdated =>
17      FirebaseRemoteConfig.instance.onConfigUpdated;
18
19  String get titleForMainPage => FirebaseRemoteConfig.instance.getString(
20        'title_for_main_page',
21      );
22
23  bool get enableSecondButton => FirebaseRemoteConfig.instance.getBool(
24        'enable_second_button',
25      );
26}

Тут у нас будет находиться:

  • init() — статичный метод для инициализации FirebaseRemoteConfig.

  • onConfigUpdated — геттер для получения стрима всех наших ключей.

  • titleForMainPage — геттер для получения заголовка на главном экране.

  • enableSecondButton — геттер для получения конфига включения второй кнопки.

В init() мы настраиваем FirebaseRemoteConfig для приложения, то есть выставляем правила обновления конфигов по переданным параметрам:

  • fetchTimeout — максимальное время ожидания ответа при получении конфига с удалённого сервера (сервиса Remote Config).

  • minimumFetchInterval — максимальный срок хранения кешированного конфига до того, как он станет устаревшей.

И выставим значения по умолчанию для наших параметров, например, если они не пришли или были удалены из сервиса Remote Config. setDefaults — выставим значения по умолчанию для ключей title_for_main_page и enable_second_button.

Используем созданный репозиторий

Во-первых, проинициализируем созданный репозиторий:

1Future<void> main() async {
2  WidgetsFlutterBinding.ensureInitialized();
3
4  await Firebase.initializeApp(
5    options: DefaultFirebaseOptions.currentPlatform,
6  );
7
8  ConfigRepository.init();
9
10  runApp(const MyApp());
11}

Далее напишем простой пример на StatefulWidget, который подпишется на стрим и будет обновлять значения, полученные из Remote Config.

Код
1
2class MainPage extends StatefulWidget {
3  const MainPage({super.key});
4
5  @override
6  State<MainPage> createState() => _MainPageState();
7}
8
9class _MainPageState extends State<MainPage> {
10  StreamSubscription? _subscription;
11
12  late ConfigRepository _configRepository;
13
14  late String titleForMainPageValue;
15  late bool enableSecondButtonValue;
16
17  @override
18  void initState() {
19    _configRepository = ConfigRepository();
20    _subscription = _configRepository.onConfigUpdated.listen(
21      (_) => updateValues(),
22    );
23    super.initState();
24  }
25
26  void updateValues() => setState(
27        () {
28          titleForMainPageValue = _configRepository.titleForMainPage;
29          enableSecondButtonValue = _configRepository.enableSecondButton;
30        },
31      );
32
33  @override
34  Widget build(BuildContext context) => Scaffold(
35        appBar: AppBar(
36          title: Text(titleForMainPageValue),
37        ),
38        body: Center(
39          child: Column(
40            mainAxisSize: MainAxisSize.min,
41            children: [
42              Text(titleForMainPageValue),
43              TextButton(
44                onPressed: () => updateValues(),
45                child: Text('Обновить значения'),
46              ),
47              if (enableSecondButtonValue)
48                TextButton(
49                  onPressed: () => doSomething(),
50                  child: Text('Новая кнопка'),
51                ),
52            ],
53          ),
54        ),
55      );
56
57  @override
58  void dispose() {
59    _subscription?.cancel();
60    _subscription = null;
61    super.dispose();
62  }
63}

 

Запустим приложение. Мы можем увидеть следующее: когда мы обновим значение любого параметра в админке Firebase, приложение должно отловить изменение и обновить состояние Stateful-виджета, то есть включить вторую кнопку или поменять заголовок на главной странице.

Дополнительная информация

Чтобы получить значения параметров из серверной части Remote Config, вызовите метод fetch(). А если вы хотите получить и активировать значение за один вызов, можно вызвать метод fetchAndActivate().

Важно

Важно

Удалённая конфигурация — очень сильный инструмент, который может изменить приложение до неузнаваемости.

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

Firebase Hosting

Это облачный сервер от Google, который позволяет размещать веб-приложения или собственную серверную часть, используя глобальную сеть CDN.

Так как Flutter — кросс-платформенный фреймворк и с его помощью можно написать веб-приложение, а также серверную часть, мы немного затронем и эту тему.

Hosting не требует добавления dart/flutter-пакета или глобального добавления Firebase, то есть он работает независимо от Flutter. Для того чтобы показать это, создадим новый проект Flutter и новый проект Firebase.

Создадим и подготовим проект

Для примера создадим новый Flutter-проект при помощи команды:

1flutter create firebase_example

Зайдём в него:

1cd firebase_example

И соберём веб-приложение, выполнив следующую команду:

1flutter build web

Пока этих шагов достаточно. Перейдём в консоль Firebase.

Настроим веб-приложение в Firebase

Зайдём в консоль Firebase и на главной странице нашего проекта выберем регистрацию веб-приложения. Firebase предложит пройти три шага:

  1. Выбрать название приложения и отметить галочкой подключение Firebase Hosting.

  2. Добавить Firebase SDK.

  3. Установить или обновить интерфейс командной строки Firebase с помощью npm — пакетного менеджера для JavaScript-библиотек:

1npm install -g firebase-tools 

В нашем примере я добавлю Firebase SDK в собранный проект с помощью скрипта. Для этого нужно зайти в файл index.html по пути firebase_example/build/web/index.html и добавить внутрь конструкции <body> скопированный скрипт, предложенный в консоли Firebase.

Примечание

Примечание

Если у вас не установлен npm, это можно сделать через менеджер пакетов Homebrew с помощью команды brew install npm. Или посмотрите инструкцию на официальном сайте.

Вход в Firebase и инициализация Firebase Hosting

Теперь выполним шаги, которые были описаны в самом начале параграфа, если вы их ещё не выполнили.

Войдём в Firebase-аккаунт при помощи команды:

1firebase login

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

Проинициализируем Firebase Hosting в проекте, выполнив команду:

1firebase init hosting

В диалоге вам предложат создать новый проект или выбрать существующий. Так как мы создали проект в самом начале, выберем его. Эта команда создаст два файла: .firebaserc с проектом и firebase.json с конфигурацией проекта.

Перейдём в файл firebase.json и укажем ему путь до нашего проекта. В параметре public меняем значение на build/web.

Деплой

Все необходимые шаги выполнены, теперь можно приступить к развёртыванию нашего веб-приложения на хостинге Firebase. Достаточно выполнить команду:

1firebase deploy

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

Дополнительная информация

После каждого изменения, которое вы хотите увидеть на сайте, нужно собирать проект под web заново и выполнять команду firebase deploy. Все предыдущие версии будут отображаться в консоли Firebase.

Также можно настроить пайплайн для сервисов VCS, который будет автоматически деплоить проект в Firebase Hosting, после появления новых пушей. Но на этом фокусироваться не будем.


Вот мы и разобрали основные сервисы Firebase! Вы научились:

  • отправлять ошибки в Firebase Crashlytics;

  • собирать технические и продуктовые метрики с помощью Firebase Analytics;

  • добавлять логику авторизации в проект с помощью Firebase Authentication;

  • удалённо настраивать приложения c помощью Remote Config;

  • подключать базу данных Firebase Firestore;

  • деплоить веб-приложение Flutter.

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

В следующем параграфе мы рассмотрим нюансы навигации во Flutter — в вебе и мобильных приложениях.

Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E
Предыдущий параграф4.5. Продвинутые библиотеки для получения данных
Следующий параграф4.7. Продвинутая навигация: в вебе и на мобильных платформах