Добавьте поток аутентификации пользователя в приложение Flutter с помощью FirebaseUI.

1. Прежде чем начать

В этой лабораторной работе вы узнаете, как добавить аутентификацию Firebase в приложение Flutter с помощью пакета FlutterFire UI. С помощью этого пакета вы добавите в приложение Flutter как аутентификацию по электронной почте и паролю, так и аутентификацию через Google Sign In. Вы также узнаете, как настроить проект Firebase и использовать интерфейс командной строки FlutterFire для инициализации Firebase в приложении Flutter.

Предпосылки

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

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

Что вы создадите

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

6604fc9157f2c6ae.pngeab9509a41074930.pngda49189a5838e0bb.pngb2ccfb3632b77878.png

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

В этой лабораторной работе рассматриваются следующие вопросы:

  • Добавление Firebase в приложение Flutter
  • Настройка консоли Firebase
  • Использование Firebase CLI для добавления Firebase в ваше приложение
  • Использование FlutterFire CLI для генерации конфигурации Firebase в Dart
  • Добавление аутентификации Firebase в ваше приложение Flutter
  • Настройка аутентификации Firebase в консоли
  • Добавление входа по электронной почте и паролю с помощью пакета firebase_ui_auth
  • Добавление регистрации пользователя с помощью пакета firebase_ui_auth
  • Добавление страницы «Забыли пароль?»
  • Добавление входа через Google с помощью firebase_ui_auth
  • Настройка приложения для работы с несколькими поставщиками услуг входа.
  • Добавление экрана профиля пользователя в ваше приложение с помощью пакета firebase_ui_auth

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

Что вам понадобится

  • Практические знания Flutter и установленного SDK
  • Текстовый редактор (Flutter поддерживает JetBrains IDE, Android Studio и VS Code)
  • Браузер Google Chrome или другая предпочитаемая вами платформа для разработки Flutter. (Некоторые команды терминала в этой практической работе предполагают, что вы запускаете приложение в Chrome.)

2. Создайте и настройте проект Firebase.

Первая задача, которую вам необходимо выполнить, — создание проекта Firebase в веб-консоли Firebase.

Создать проект Firebase

  1. Войдите в консоль Firebase, используя свою учетную запись Google.
  2. Нажмите кнопку, чтобы создать новый проект, а затем введите имя проекта (например, FlutterFire-UI-Codelab ).
  3. Нажмите «Продолжить» .
  4. При появлении соответствующего запроса ознакомьтесь с условиями Firebase и примите их, а затем нажмите кнопку «Продолжить» .
  5. (Необязательно) Включите помощь ИИ в консоли Firebase (так называемая «Gemini в Firebase»).
  6. Для этой лабораторной работы вам не понадобится Google Analytics, поэтому отключите опцию Google Analytics.
  7. Нажмите «Создать проект» , дождитесь завершения подготовки проекта, а затем нажмите «Продолжить» .

Дополнительную информацию о проектах Firebase см. в разделе Понимание проектов Firebase .

Включить вход по электронной почте для аутентификации Firebase

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

Аутентификацию Firebase необходимо включить с помощью консоли Firebase, после включения потребуется специальная настройка.

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

  1. В консоли Firebase разверните меню «Сборка» на левой панели.
  2. Нажмите «Аутентификация» , затем нажмите кнопку « Начать» , затем вкладку «Способ входа» (или перейдите сразу на вкладку «Способ входа »).
  3. Нажмите «Электронная почта/Пароль» в списке поставщиков входа , установите переключатель «Включить» в положение «Вкл.», а затем нажмите « Сохранить ».

58e3e3e23c2f16a4.png

3. Настройте приложение Flutter

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

Получить стартовый код

Клонируйте репозиторий GitHub из командной строки:

git clone https://github.com/flutter/codelabs.git flutter-codelabs

Альтернативно, если у вас установлен инструмент CLI GitHub :

gh repo clone flutter/codelabs flutter-codelabs

Пример кода необходимо клонировать в каталог flutter-codelabs на вашем компьютере, содержащий код для набора лабораторных работ. Код этой лабораторной работы находится в подкаталоге flutter-codelabs/firebase-auth-flutterfire-ui .

Каталог flutter-codelabs/firebase-auth-flutterfire-ui содержит два проекта Flutter. Один называется complete , а другой — start . В каталоге start находится незавершённый проект, и именно на него вы потратите больше всего времени.

cd flutter-codelabs/firebase-auth-flutterfire-ui/start

Если вы хотите перейти вперед или посмотреть, как что-то должно выглядеть после завершения, загляните в каталог с именем complete для перекрестной ссылки.

Если вы хотите следовать инструкциям в этой лабораторной работе и добавлять код самостоятельно, начните с приложения Flutter по адресу flutter-codelabs/firebase-auth-flutterfire-ui/start и добавляйте код в этот проект по мере выполнения лабораторной работы. Откройте или импортируйте этот каталог в предпочитаемую вами IDE.

Установить Firebase CLI

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

Существует множество способов установки CLI. Ознакомьтесь со всеми доступными вариантами для вашей операционной системы на сайте firebase.google.com/docs/cli .

После установки CLI необходимо пройти аутентификацию в Firebase.

  1. Войдите в Firebase, используя свою учетную запись Google, выполнив следующую команду:
    firebase login
    
  2. Эта команда подключает ваш локальный компьютер к Firebase и предоставляет вам доступ к вашим проектам Firebase.
  3. Проверьте, что CLI установлен правильно и имеет доступ к вашей учётной записи, выведя список проектов Firebase. Выполните следующую команду:
    firebase projects:list
    
  4. Отображаемый список должен совпадать с проектами Firebase, перечисленными в консоли Firebase . Вы должны увидеть как минимум flutterfire-ui-codelab.

Установите FlutterFire CLI

FlutterFire CLI — это инструмент, упрощающий процесс установки Firebase на всех поддерживаемых платформах в вашем приложении Flutter. Он создан на основе Firebase CLI.

Сначала установите CLI:

dart pub global activate flutterfire_cli

Убедитесь, что CLI установлен. Выполните следующую команду и убедитесь, что CLI выводит меню справки.

flutterfire --help

Добавьте свой проект Firebase в приложение Flutter

Настроить FlutterFire

Вы можете использовать FlutterFire для генерации необходимого кода Dart для использования Firebase в вашем приложении Flutter.

flutterfire configure

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

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

  1. Выберите нужный проект. В данном случае это flutterfire-ui-codelab 1359cdeb83204baa.png
  2. Выберите платформы, которые вы хотите использовать. В этой лабораторной работе приведены инструкции по настройке аутентификации Firebase для Flutter для веб-приложений, iOS и Android, но вы можете настроить свой проект Firebase для использования всех вариантов. 301c9534f594f472.png
  3. На этом снимке экрана показан результат выполнения в конце процесса. Если вы знакомы с Firebase, то заметите, что вам не пришлось создавать приложения платформы (например, приложение для Android) в консоли — интерфейс командной строки FlutterFire сделал это за вас. 12199a85ade30459.png

После завершения откройте приложение Flutter в текстовом редакторе. Интерфейс командной строки FlutterFire изменил файл firebase_options.dart . Этот файл содержит класс FirebaseOptions , содержащий статические переменные, которые содержат конфигурацию Firebase, необходимую для каждой платформы. Если при запуске команды flutterfire configure вы выбрали все платформы, вы увидите статические значения web , android , ios и macos .

lib/firebase_options.dart

import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
    show defaultTargetPlatform, kIsWeb, TargetPlatform;

class DefaultFirebaseOptions {
  static FirebaseOptions get currentPlatform {
    if (kIsWeb) {
      return web;
    }

    switch (defaultTargetPlatform) {
      case TargetPlatform.android:
        return android;
      case TargetPlatform.iOS:
        return ios;
      case TargetPlatform.macOS:
        return macos;
      default:
        throw UnsupportedError(
          'DefaultFirebaseOptions are not supported for this platform.',
        );
    }
  }

  static const FirebaseOptions web = FirebaseOptions(
    apiKey: 'AIzaSyCqFjCV_9CZmYeIvcK9FVy4drmKUlSaIWY',
    appId: '1:963656261848:web:7219f7fca5fc70afb237ad',
    messagingSenderId: '963656261848',
    projectId: 'flutterfire-ui-codelab',
    authDomain: 'flutterfire-ui-codelab.firebaseapp.com',
    storageBucket: 'flutterfire-ui-codelab.firebasestorage.app',
    measurementId: 'G-DGF0CP099H',
  );

  static const FirebaseOptions android = FirebaseOptions(
    apiKey: 'AIzaSyDconZaCQpkxIJ5KQBF-3tEU0rxYsLkIe8',
    appId: '1:963656261848:android:c939ccc86ab2dcdbb237ad',
    messagingSenderId: '963656261848',
    projectId: 'flutterfire-ui-codelab',
    storageBucket: 'flutterfire-ui-codelab.firebasestorage.app',
  );

  static const FirebaseOptions ios = FirebaseOptions(
    apiKey: 'AIzaSyBqLWsqFjYAdGyihKTahMRDQMo0N6NVjAs',
    appId: '1:963656261848:ios:d9e01cfe8b675dfcb237ad',
    messagingSenderId: '963656261848',
    projectId: 'flutterfire-ui-codelab',
    storageBucket: 'flutterfire-ui-codelab.firebasestorage.app',
    iosClientId: '963656261848-v7r3vq1v6haupv0l1mdrmsf56ktnua60.apps.googleusercontent.com',
    iosBundleId: 'com.example.complete',
  );

  static const FirebaseOptions macos = FirebaseOptions(
    apiKey: 'AIzaSyBqLWsqFjYAdGyihKTahMRDQMo0N6NVjAs',
    appId: '1:963656261848:ios:d9e01cfe8b675dfcb237ad',
    messagingSenderId: '963656261848',
    projectId: 'flutterfire-ui-codelab',
    storageBucket: 'flutterfire-ui-codelab.firebasestorage.app',
    iosClientId: '963656261848-v7r3vq1v6haupv0l1mdrmsf56ktnua60.apps.googleusercontent.com',
    iosBundleId: 'com.example.complete',
  );
}

В Firebase слово «приложение» используется для обозначения конкретной сборки для конкретной платформы в проекте Firebase. Например, проект Firebase под названием FlutterFire-ui-codelab имеет несколько приложений: одно для Android, одно для iOS, одно для macOS и одно для веб-приложений.

Метод DefaultFirebaseOptions.currentPlatform использует перечисление TargetPlatform , предоставляемое Flutter, для определения платформы, на которой работает ваше приложение, а затем возвращает значения конфигурации Firebase, необходимые для корректного приложения Firebase.

Добавьте пакеты Firebase в приложение Flutter

Последний шаг настройки — добавление соответствующих пакетов Firebase в ваш проект Flutter. Файл firebase_options.dart должен содержать ошибки, поскольку он использует пакеты Firebase, которые ещё не добавлены. В терминале убедитесь, что вы находитесь в корневом каталоге проекта Flutter по адресу flutter-codelabs/firebase-emulator-suite/start . Затем выполните три следующие команды:

flutter pub add firebase_core firebase_auth firebase_ui_auth

На данном этапе это единственные пакеты, которые вам понадобятся.

Инициализировать Firebase

Чтобы использовать добавленные пакеты и DefaultFirebaseOptions.currentPlatform, обновите код в функции main в файле main.dart .

lib/main.dart

import 'package:firebase_core/firebase_core.dart';                  // Add this import
import 'package:flutter/material.dart';

import 'app.dart';
import 'firebase_options.dart';                                     // And this import

// TODO(codelab user): Get API key
const clientId = 'YOUR_CLIENT_ID';

void main() async {
  // Add from here...
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  // To here.

  runApp(const MyApp(clientId: clientId));
}

Этот код делает две вещи.

  1. WidgetsFlutterBinding.ensureInitialized() сообщает Flutter, что не следует запускать код виджета приложения до полной загрузки фреймворка Flutter. Firebase использует собственные каналы платформы, для которых требуется запущенный фреймворк.
  2. Firebase.initializeApp устанавливает соединение между вашим приложением Flutter и проектом Firebase. DefaultFirebaseOptions.currentPlatform импортируется из сгенерированного нами файла firebase_options.dart . Это статическое значение определяет, на какой платформе вы работаете, и передаёт соответствующие ключи Firebase.

4. Добавьте начальную страницу аутентификации Firebase UI.

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

Материал или приложение из Купертино

Пользовательский интерфейс FlutterFire требует, чтобы ваше приложение было обернуто в MaterialApp или CupertinoApp . В зависимости от вашего выбора, пользовательский интерфейс автоматически отразит различия между виджетами Material или Cupertino. Для этой практической работы используйте MaterialApp , который уже добавлен в приложение в app.dart .

lib/app.dart

import 'package:flutter/material.dart';

import 'auth_gate.dart';

class MyApp extends StatelessWidget {
  const MyApp({super.key, required this.clientId});

  final String clientId;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: AuthGate(clientId: clientId),
    );
  }
}

Проверить состояние аутентификации

Прежде чем отображать экран входа, необходимо определить, аутентифицирован ли пользователь. Самый распространённый способ проверки — прослушивание authStateChanges FirebaseAuth с помощью плагина Firebase Auth .

В приведенном выше примере кода MaterialApp создает виджет AuthGate в своем методе build . (Это пользовательский виджет, не предоставляемый FlutterFire UI.)

Этот виджет необходимо обновить, включив в него поток authStateChanges .

API authStateChanges возвращает Stream , содержащий либо текущий пользователь (если он вошел в систему), либо null, если он не вошел. Чтобы подписаться на это состояние в нашем приложении, вы можете использовать виджет Flutter StreamBuilder и передать ему поток.

StreamBuilder — это виджет, который строится на основе последнего снимка данных из потока , который вы ему передадите. Он автоматически перестраивается, когда Stream отправляет новый снимок.

Обновите код в auth_gate.dart .

lib/auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider; // Add this import
import 'package:firebase_ui_auth/firebase_ui_auth.dart';                  // And this import
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key, required this.clientId});

  final String clientId;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(                                       // Modify from here...
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(providers: []);
        }

        return const HomeScreen();
      },
    );                                                                 // To here.
  }
}
  • StreamBuilder.stream передается FirebaseAuth.instance.authStateChanged , вышеупомянутому потоку, который вернет объект Firebase User , если пользователь прошел аутентификацию, в противном случае он вернет null .
  • Далее код использует snapshot.hasData для проверки того, содержит ли значение из потока объект User .
  • Если его нет, будет возвращен виджет SignInScreen . На данный момент этот экран не выполняет никаких действий, он будет обновлён на следующем этапе.
  • В противном случае он возвращает HomeScreen — основную часть приложения, доступ к которой имеют только аутентифицированные пользователи.

SignInScreen — это виджет из пакета FlutterFire UI. Он будет рассмотрен на следующем этапе этой лабораторной работы. После запуска приложения на этом этапе вы увидите пустой экран входа.

5. Экран входа

Виджет SignInScreen , предоставляемый FlutterFire UI, добавляет следующие функции:

  • Позволяет пользователям входить в систему
  • Если пользователи забыли свой пароль, они могут нажать «Забыли пароль?» и перейти к форме для сброса пароля.
  • Если пользователь еще не зарегистрирован, он может нажать «Зарегистрироваться» и перейти к другой форме, позволяющей ему зарегистрироваться.

Опять же, для этого потребуется всего пара строк кода. Вспомните код в виджете AuthGate :

lib/auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key, required this.clientId});

  final String clientId;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(providers: [EmailAuthProvider()]);  // Modify this line
        }

        return const HomeScreen();
      },
    );
  }
}

Виджет SignInScreen и его аргумент providers — это единственный код, необходимый для реализации всех вышеупомянутых функций. Теперь вы должны увидеть экран входа с полями для ввода адреса электронной почты и пароля, а также кнопкой «Войти».

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

Настройте экран входа

headerBuilder

Используя аргумент SignInScreen.headerBuilder , вы можете добавить любые виджеты над формой входа. Этот виджет отображается только на узких экранах, например, на мобильных устройствах. На широких экранах можно использовать SignInScreen.sideBuilder , который будет рассмотрен далее в этой практической работе.

Обновите файл lib/auth_gate.dart следующим кодом:

lib/auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key, required this.clientId});

  final String clientId;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(                                         // Modify from here...
            providers: [EmailAuthProvider()],
            headerBuilder: (context, constraints, shrinkOffset) {
              return Padding(
                padding: const EdgeInsets.all(20),
                child: AspectRatio(
                  aspectRatio: 1,
                  child: Image.asset('assets/flutterfire_300x.png'),
                ),
              );
            },
          );                                                           // To here.
        }

        return const HomeScreen();
      },
    );
  }
}```

The headerBuilder argument requires a function of the type HeaderBuilder, which
is defined in the FlutterFire UI package.

```dart
typedef HeaderBuilder = Widget Function(
 BuildContext context,
 BoxConstraints constraints,
 double shrinkOffset,
);

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

73d7548d91bbd2ab.png

Конструктор субтитров

Экран входа предоставляет три дополнительных параметра, которые позволяют настраивать экран: subtitleBuilder , footerBuilder и sideBuilder .

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

Обновите код в auth_gate.dart для использования subtitleBuilder .

lib/auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key, required this.clientId});

  final String clientId;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [EmailAuthProvider()],
            headerBuilder: (context, constraints, shrinkOffset) {
              return Padding(
                padding: const EdgeInsets.all(20),
                child: AspectRatio(
                  aspectRatio: 1,
                  child: Image.asset('assets/flutterfire_300x.png'),
                ),
              );
            },
            subtitleBuilder: (context, action) {                     // Add from here...
              return Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: action == AuthAction.signIn
                    ? const Text('Welcome to FlutterFire, please sign in!')
                    : const Text('Welcome to Flutterfire, please sign up!'),
              );
            },                                                       // To here.
          );
        }

        return const HomeScreen();
      },
    );
  }
}

Аргумент footerBuilder аналогичен аргументу subtitleBuilder. Он не использует BoxConstraints и shrinkOffset , поскольку предназначен для текста, а не изображений. Конечно, вы можете добавить любой виджет по своему усмотрению.

Добавьте нижний колонтитул на экран входа с помощью этого кода.

lib/auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key, required this.clientId});

  final String clientId;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [EmailAuthProvider()],
            headerBuilder: (context, constraints, shrinkOffset) {
              return Padding(
                padding: const EdgeInsets.all(20),
                child: AspectRatio(
                  aspectRatio: 1,
                  child: Image.asset('assets/flutterfire_300x.png'),
                ),
              );
            },
            subtitleBuilder: (context, action) {
              return Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: action == AuthAction.signIn
                    ? const Text('Welcome to FlutterFire, please sign in!')
                    : const Text('Welcome to Flutterfire, please sign up!'),
              );
            },
            footerBuilder: (context, action) {                       // Add from here...
              return const Padding(
                padding: EdgeInsets.only(top: 16),
                child: Text(
                  'By signing in, you agree to our terms and conditions.',
                  style: TextStyle(color: Colors.grey),
                ),
              );
            },                                                       // To here.
          );
        }

        return const HomeScreen();
      },
    );
  }
}

Боковой строитель

Аргумент SignInScreen.sidebuilder принимает обратный вызов, и на этот раз аргументами этого обратного вызова являются BuildContext и double shrinkOffset . Виджет, возвращаемый sideBuilder , будет отображаться слева от формы входа и только на широких экранах. Фактически это означает, что виджет будет отображаться только в десктопных и веб-приложениях.

Внутренне FlutterFire UI использует точку останова, чтобы определить, следует ли отображать содержимое заголовка (на высоких экранах, например, мобильных устройствах) или боковое содержимое (на широких экранах, настольных компьютерах или веб-браузерах). В частности, если ширина экрана превышает 800 пикселей, отображается боковое содержимое, а содержимое заголовка — нет. Если ширина экрана меньше 800 пикселей, происходит обратное.

Обновите код в auth_gate.dart, чтобы добавить виджеты sideBuilder .

lib/auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key, required this.clientId});

  final String clientId;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [EmailAuthProvider()],
            headerBuilder: (context, constraints, shrinkOffset) {
              return Padding(
                padding: const EdgeInsets.all(20),
                child: AspectRatio(
                  aspectRatio: 1,
                  child: Image.asset('assets/flutterfire_300x.png'),
                ),
              );
            },
            subtitleBuilder: (context, action) {
              return Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: action == AuthAction.signIn
                    ? const Text('Welcome to FlutterFire, please sign in!')
                    : const Text('Welcome to Flutterfire, please sign up!'),
              );
            },
            footerBuilder: (context, action) {
              return const Padding(
                padding: EdgeInsets.only(top: 16),
                child: Text(
                  'By signing in, you agree to our terms and conditions.',
                  style: TextStyle(color: Colors.grey),
                ),
              );
            },
            sideBuilder: (context, shrinkOffset) {
              return Padding(
                padding: const EdgeInsets.all(20),
                child: AspectRatio(
                  aspectRatio: 1,
                  child: Image.asset('flutterfire_300x.png'),
                ),
              );
            },
          );
        }

        return const HomeScreen();
      },
    );
  }
}

Теперь при увеличении ширины окна ваше приложение должно выглядеть так (если вы используете Flutter Web или MacOS).

8dc60b4e5d7dd2d0.png

Создать пользователя

На этом этапе весь код для этого экрана готов. Однако перед входом в систему необходимо создать пользователя. Это можно сделать на экране «Регистрация» или в консоли Firebase.

Чтобы использовать консоль:

  1. Перейдите в таблицу «Пользователи» в консоли Firebase . Выберите «flutterfire-ui-codelab» или другой проект, если вы использовали другое имя. Вы увидите следующую таблицу: f038fd9a58ed60d9.png
  2. Нажмите кнопку «Добавить пользователя». 2d78390d4c5dbbfa.png
  3. Введите адрес электронной почты и пароль для нового пользователя. Это могут быть поддельные адрес электронной почты и пароль, как на изображении ниже. Это сработает, но функция «Забыли пароль?» не будет работать, если вы используете поддельный адрес электронной почты. 62ba0feb33d54add.png
  4. Нажмите «Добавить пользователя». 32b236b3ef94d4c7.png

Теперь вы можете вернуться в приложение Flutter и авторизовать пользователя на странице входа. Ваше приложение должно выглядеть так:

dd43d260537f3b1a.png

6. Экран профиля

FlutterFire UI также предоставляет виджет ProfileScreen , который, опять же, предоставляет вам массу функциональных возможностей в нескольких строках кода.

Добавить виджет ProfileScreen

Откройте файл home.dart в текстовом редакторе. Добавьте в него следующий код:

lib/home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => const ProfileScreen(),
                ),
              );
            },
          ),
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            SizedBox(width: 250, child: Image.asset('assets/dash.png')),
            Text('Welcome!', style: Theme.of(context).textTheme.displaySmall),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

Новый код, который стоит отметить, — это обратный вызов, передаваемый методу IconButton.isPressed . При нажатии на эту IconButton приложение создаёт новый анонимный маршрут и переходит к нему. Этот маршрут отобразит виджет ProfileScreen , возвращаемый обратным вызовом MaterialPageRoute.builder .

Перезагрузите приложение и нажмите на значок в правом верхнем углу (на панели приложения), после чего откроется следующая страница:

36487fc4ab4f26a7.png

Это стандартный пользовательский интерфейс, предоставляемый страницей FlutterFire UI. Все кнопки и текстовые поля подключены к Firebase Auth и работают без проблем. Например, вы можете ввести имя в текстовое поле «Имя», и FlutterFire UI вызовет метод FirebaseAuth.instance.currentUser?.updateDisplayName , который сохранит это имя в Firebase.

Выход

Сейчас, если вы нажмёте кнопку «Выйти», приложение не изменится. Оно выполнит выход из учётной записи, но не вернёт вас к виджету AuthGate. Для этого используйте параметр ProfileScreen.actions.

Сначала обновите код в home.dart.

lib/home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => ProfileScreen(
                    actions: [
                      SignedOutAction((context) {
                        Navigator.of(context).pop();
                      }),
                    ],
                  ),
                ),
              );
            },
          ),
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            SizedBox(width: 250, child: Image.asset('assets/dash.png')),
            Text('Welcome!', style: Theme.of(context).textTheme.displaySmall),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

Теперь, когда вы создаёте экземпляр ProfileScreen , вы также передаёте ему список действий в аргумент ProfileScreen.actions . Эти действия относятся к типу FlutterFireUiAction . Существует множество различных классов, являющихся подтипами FlutterFireUiAction , и, как правило, они используются для того, чтобы сообщить приложению, как реагировать на различные изменения состояния аутентификации. Действие SignedOutAction вызывает функцию обратного вызова, которую вы ему передаёте, когда состояние аутентификации Firebase меняется на currentUser, равный null.

Добавление обратного вызова, который вызывает Navigator.of(context).pop() при срабатывании SignedOutAction , позволяет приложению перейти на предыдущую страницу. В этом примере приложения есть только один постоянный маршрут, который отображает экран входа, если пользователь не вошел в систему, и домашнюю страницу, если пользователь вошел. Поскольку это происходит при выходе пользователя из системы, приложение отображает экран входа.

Настройте страницу профиля

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

lib/home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => ProfileScreen(
                    appBar: AppBar(title: const Text('User Profile')),
                    actions: [
                      SignedOutAction((context) {
                        Navigator.of(context).pop();
                      }),
                    ],
                  ),
                ),
              );
            },
          ),
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            SizedBox(width: 250, child: Image.asset('assets/dash.png')),
            Text('Welcome!', style: Theme.of(context).textTheme.displaySmall),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

Аргумент ProfileScreen.appBar принимает виджет AppBar из пакета Flutter Material, поэтому его можно обрабатывать как любой другой AppBar , созданный вами и переданный в Scaffold . В этом примере сохраняется стандартная функциональность автоматического добавления кнопки «Назад», а у экрана теперь есть заголовок.

Добавить детей на экран профиля

Виджет ProfileScreen также имеет необязательный аргумент children. Этот аргумент принимает список виджетов, которые будут размещены вертикально внутри виджета Column , который уже используется для построения ProfileScreen . Этот виджет Column в методе сборки ProfileScreen разместит переданные ему дочерние элементы над кнопкой «Выйти».

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

lib/home.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => ProfileScreen(
                    appBar: AppBar(title: const Text('User Profile')),
                    actions: [
                      SignedOutAction((context) {
                        Navigator.of(context).pop();
                      }),
                    ],
                    children: [
                      const Divider(),
                      Padding(
                        padding: const EdgeInsets.all(2),
                        child: AspectRatio(
                          aspectRatio: 1,
                          child: Image.asset('flutterfire_300x.png'),
                        ),
                      ),
                    ],
                  ),
                ),
              );
            },
          ),
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            SizedBox(width: 250, child: Image.asset('assets/dash.png')),
            Text('Welcome!', style: Theme.of(context).textTheme.displaySmall),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

Перезагрузите приложение, и вы увидите это на экране:

ebe5792b765dbf87.png

7. Мультиплатформенный вход через Google Auth

FlutterFire UI также предоставляет виджеты и функции для аутентификации у сторонних поставщиков, таких как Google, Twitter, Facebook, Apple и GitHub.

Для интеграции с аутентификацией Google установите официальный плагин firebase_ui_oauth_google и его зависимости, которые будут управлять нативным процессом аутентификации. В терминале перейдите в корневой каталог вашего проекта Flutter и введите следующую команду:

flutter pub add google_sign_in firebase_ui_oauth_google

Включить поставщика входа Google

Затем включите провайдера Google в консоли Firebase :

  1. Перейдите на экран «Поставщики аутентификации» в консоли.
  2. Нажмите «Добавить нового поставщика». 8286fb28be94bf30.png
  3. Выберите «Google». c4e28e6f4974be7f.png
  4. Переключите переключатель «Включить» и нажмите «Сохранить». e74ff86990763826.png
  5. Если появится модальное окно с информацией о загрузке файлов конфигурации, нажмите «Готово».
  6. Подтвердите, что поставщик входа Google добавлен. 5329ce0543c90d95.png

Добавить кнопку входа в Google

При включенном входе через Google добавьте виджет, необходимый для отображения стилизованной кнопки входа Google на экране входа. Перейдите к файлу auth_gate.dart и обновите код следующим образом:

lib/auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:firebase_ui_oauth_google/firebase_ui_oauth_google.dart';  // Add this import
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key, required this.clientId});

  final String clientId;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [
              EmailAuthProvider(),
              GoogleProvider(clientId: clientId),                         // Add this line
            ],
            headerBuilder: (context, constraints, shrinkOffset) {
              return Padding(
                padding: const EdgeInsets.all(20),
                child: AspectRatio(
                  aspectRatio: 1,
                  child: Image.asset('assets/flutterfire_300x.png'),
                ),
              );
            },
            subtitleBuilder: (context, action) {
              return Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: action == AuthAction.signIn
                    ? const Text('Welcome to FlutterFire, please sign in!')
                    : const Text('Welcome to Flutterfire, please sign up!'),
              );
            },
            footerBuilder: (context, action) {
              return const Padding(
                padding: EdgeInsets.only(top: 16),
                child: Text(
                  'By signing in, you agree to our terms and conditions.',
                  style: TextStyle(color: Colors.grey),
                ),
              );
            },
            sideBuilder: (context, shrinkOffset) {
              return Padding(
                padding: const EdgeInsets.all(20),
                child: AspectRatio(
                  aspectRatio: 1,
                  child: Image.asset('flutterfire_300x.png'),
                ),
              );
            },
          );
        }

        return const HomeScreen();
      },
    );
  }
}

Единственный новый код здесь — это добавление GoogleProvider(clientId: "YOUR_WEBCLIENT_ID") в конфигурацию виджета SignInScreen .

После этого перезагрузите приложение, и вы увидите кнопку входа в Google.

aca71a46a011bfb5.png

Настроить кнопку входа

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

  1. Перейдите на страницу «Поставщики аутентификации» в консоли Firebase .
  2. Нажмите на поставщика Google. 9b3a325c5eca6e49.png
  3. Нажмите на панель расширения «Конфигурация Web SDK».
  4. Скопируйте значение из «Идентификатора веб-клиента». 711a79f0d931c60f.png
  5. Вернитесь в текстовый редактор и обновите экземпляр GoogleProvider в файле auth_gate.dart , передав этот идентификатор именованному параметру clientId .
GoogleProvider(
   clientId: "YOUR_WEBCLIENT_ID"
)

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

14e73e3c9de704bb.png

Настроить iOS

Чтобы это работало на iOS, необходимо выполнить дополнительную настройку.

  1. Перейдите на экран настроек проекта в консоли Firebase . Там вы увидите карточку со списком ваших приложений Firebase, которая выглядит следующим образом: fefa674acbf213cc.png
  2. Выберите iOS. Обратите внимание, что имя вашего приложения будет отличаться от показанного на скриншоте. Если вы использовали проект flutter-codelabs/firebase-auth-flutterfire-ui/start для выполнения этой практической работы, то там, где на скриншоте написано «complete», у вас будет написано «start».
  3. Нажмите кнопку с надписью GoogleServices-Info.plist чтобы загрузить необходимый файл конфигурации. f89b3192871dfbe3.png
  4. Перетащите загруженный файл в каталог /ios/Runner вашего проекта Flutter.
  5. Откройте Xcode, выполнив следующую команду терминала из корня вашего проекта: open ios/Runner.xcworkspace
  6. Щелкните правой кнопкой мыши по каталогу Runner и выберите Добавить файлы в «Runner». 858986063a4c5201.png
  7. Выберите GoogleService-Info.plist в файловом менеджере.
  8. Вернитесь в текстовый редактор (не Xcode) и добавьте указанные ниже атрибуты CFBundleURLTypes в файл ios/Runner/Info.plist .
    <!-- Put me in the [my_project]/ios/Runner/Info.plist file -->
    <!-- Google Sign-in Section -->
    <key>CFBundleURLTypes</key>
    <array>
      <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLSchemes</key>
        <array>
          <!-- TODO Replace this value: -->
          <!-- Copied from GoogleService-Info.plist key REVERSED_CLIENT_ID -->
          <string>com.googleusercontent.apps.861823949799-vc35cprkp249096uujjn0vvnmcvjppkn</string>
        </array>
      </dict>
    </array>
    <!-- End of the Google Sign-in Section -->
    
  9. Вам необходимо заменить GoogleProvider.clientId , добавленный в веб-настройке, на Client Id, связанный с вашим клиентским ID Firebase iOS. Этот ID можно найти в файле firebase_options.dart как часть константы iOS . Скопируйте значение, переданное в iOSClientId .
    static const FirebaseOptions ios = FirebaseOptions(
      apiKey: 'YOUR API KEY',
      appId: 'YOUR APP ID',
      messagingSenderId: '',
      projectId: 'PROJECT_ID',
      storageBucket: 'PROJECT_ID.firebasestorage.app',
      iosClientId: 'IOS CLIENT ID', // Find your iOS client Id here.
      iosBundleId: 'com.example.BUNDLE',
    );
    
  10. Вставьте это значение в переменную clientId в файле lib/main.dart .

lib/main.dart

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

import 'app.dart';
import 'firebase_options.dart';

const clientId = 'YOUR_CLIENT_ID'; // Replace this value with your Client ID.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

  runApp(const MyApp(clientId: clientId));
}

Если ваше приложение Flutter уже запущено на iOS, вам необходимо полностью завершить его работу и перезапустить. В противном случае запустите приложение на iOS.

8. Поздравляем!

Вы завершили практическую работу по созданию пользовательского интерфейса аутентификации Firebase для Flutter. Готовый код для этой практической работы можно найти в каталоге firebase-auth-flutterfire-ui/complete на GitHub .

Что мы рассмотрели

  • Настройка приложения Flutter для использования Firebase
  • Настройка проекта Firebase в консоли Firebase
  • FlutterFire CLI
  • Firebase CLI
  • Использование аутентификации Firebase
  • Использование FlutterFire UI для обработки аутентификации Firebase в вашем приложении Flutter

Следующие шаги

Узнать больше

Спарки здесь, чтобы отпраздновать вместе с вами!

2a0ad195769368b1.gif