使用 FirebaseUI 向 Flutter 应用添加用户身份验证流程

1. 准备工作

在此 Codelab 中,您将学习如何使用 FlutterFire 界面软件包将 Firebase Authentication 添加到您的 Flutter 应用。借助此软件包,您将向 Flutter 应用添加电子邮件地址/密码身份验证和 Google 登录身份验证。您还将学习如何设置 Firebase 项目,以及如何使用 FlutterFire CLI 在 Flutter 应用中初始化 Firebase。

前提条件

此 Codelab 假定您具有一些 Flutter 经验。如果没有,您可能需要先了解基础知识。以下链接非常有用:

您还应具备一定程度的 Firebase 经验,但如果您从未将 Firebase 添加到 Flutter 项目,也没有关系。如果您不熟悉 Firebase 控制台,或者第一次使用 Firebase,请先查看以下链接:

您将创建的内容

此 Codelab 将指导您使用 Firebase 进行身份验证,从而构建 Flutter 应用的身份验证流程。该应用包含登录屏幕、“注册”屏幕、密码恢复屏幕以及用户个人资料屏幕。

6604fc9157f2c6ae eab9509a41074930 da49189a5838e0bb b2ccfb3632b77878.png

学习内容

此 Codelab 涵盖了以下内容:

  • 将 Firebase 添加到 Flutter 应用
  • Firebase 控制台设置
  • 使用 Firebase CLI 将 Firebase 添加至您的应用
  • 使用 FlutterFire CLI 在 Dart 中生成 Firebase 配置
  • 将 Firebase Authentication 添加到您的 Flutter 应用
  • 在控制台中设置 Firebase Authentication
  • 使用 firebase_ui_auth 软件包添加电子邮件地址和密码登录
  • 通过 firebase_ui_auth 软件包添加用户注册
  • 添加“忘记了密码?”页面
  • 使用 firebase_ui_auth 添加 Google 登录功能
  • 配置您的应用以使用多账号登录提供方。
  • 使用 firebase_ui_auth 软件包向应用添加用户个人资料屏幕

此 Codelab 主要介绍了如何使用 firebase_ui_auth 软件包添加强大的身份验证系统。您会发现,包含上述所有功能的完整应用只需大约 100 行代码即可实现。

所需条件

  • 具备 Flutter 的应用知识以及已安装的 SDK
  • 文本编辑器(Flutter 支持 JetBrains IDE、Android Studio 和 VS Code)
  • Google Chrome 浏览器,或您偏好的 Flutter 开发目标平台。(此 Codelab 中的一些终端命令将假定您在 Chrome 上运行应用)

2. 创建和设置 Firebase 项目

您需要完成的第一项任务是在 Firebase 的 Web 控制台中创建一个 Firebase 项目。

创建 Firebase 项目

  1. 登录 Firebase
  2. 在 Firebase 控制台中,点击添加项目(或创建项目),然后输入 Firebase 项目的名称(例如“FlutterFire-UI-Codelab”)。

df42a5e3d9584b48.png

  1. 点击各个项目创建选项。如果系统提示,请接受 Firebase 条款。跳过设置 Google Analytics(分析)的步骤,因为您不会为此应用使用 Google Analytics(分析)。

d1fcec48bf251eaa.png

如需详细了解 Firebase 项目,请参阅了解 Firebase 项目

您正在构建的应用使用 Firebase Authentication 来允许用户登录您的应用。此外,它还允许新用户从 Flutter 应用注册。

Firebase Authentication 需要使用 Firebase 控制台启用,并在启用后需要特殊配置。

为 Firebase Authentication 启用电子邮件登录功能

如要允许用户登录 Web 应用,您需要先使用电子邮件地址/密码登录方法。稍后,您将添加 Google 登录方法。

  1. 在 Firebase 控制台中,展开左侧面板中的构建菜单。
  2. 点击身份验证,然后点击开始使用按钮,再点击登录方法标签页(或点击此处直接转到登录方法标签页)。
  3. 点击登录服务提供商列表中的电子邮件地址/密码,将启用开关切换到开启位置,然后点击保存58e3e3e23c2f16a4.png

3. 设置 Flutter 应用

在我们开始之前,您需要下载起始代码并安装 Firebase CLI。

获取起始代码

从命令行克隆 GitHub 代码库

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

或者,如果您已安装 GitHub 的 CLI 工具:

gh repo clone flutter/codelabs flutter-codelabs

应将示例代码克隆到计算机上的 flutter-codelabs 目录中,其中包含一系列 Codelab 的代码。此 Codelab 的代码位于 flutter-codelabs/firebase-auth-flutterfire-ui 子目录中。

目录 flutter-codelabs/firebase-auth-flutterfire-ui 包含两个 Flutter 项目。一个名为 complete,另一个名为 startstart 目录包含一个不完整的项目,而该项目将会用到最多的时间。

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

如果您想向前跳转,或者查看完成后的内容,请在名为“完成”的目录中查找以进行交叉引用。

如果您想按照此 Codelab 的说明操作,并自行添加代码,则应从位于 flutter-codelabs/firebase-auth-flutterfire-ui/start 的 Flutter 应用入手,并在整个 Codelab 中向该项目添加代码。打开该目录,或将该目录导入您的首选 IDE。

安装 Firebase CLI

Firebase CLI 提供了用于管理 Firebase 项目的工具。FlutterFire CLI 需要使用 CLI,稍后您将进行安装。

您可以通过多种方式安装 CLI。如果您使用的是 MacOS 或 Linux,最简单的方法是从终端运行此命令:

curl -sL https://firebase.tools | bash

安装 CLI 后,您必须进行 Firebase 身份验证。

  1. 运行以下命令,使用您的 Google 账号登录 Firebase:
firebase login
  1. 此命令会将您的本地机器与 Firebase 关联,并授予您对 Firebase 项目的访问权限。
  1. 列出您的 Firebase 项目,测试 CLI 是否已正确安装以及是否可以访问您的帐号。运行以下命令:
firebase projects:list
  1. 显示的列表应与 Firebase 控制台中列出的 Firebase 项目相同。您应该会看到至少 flutterfire-ui-codelab.

安装 FlutterFire CLI

FlutterFire CLI 是一款工具,有助于在您的 Flutter 应用的所有受支持平台上简化 Firebase 的安装过程。它基于 Firebase CLI 构建。

首先,安装 CLI:

dart pub global activate flutterfire_cli

确保已安装 CLI。运行以下命令并确保 CLI 输出帮助菜单。

flutterfire -—help

将 Firebase 项目添加到您的 Flutter 应用

配置 FlutterFire

您可以使用 FlutterFire 生成所需的 Dart 代码,以便在您的 Flutter 应用中使用 Firebase。

flutterfire configure

运行此命令时,系统会提示您选择要使用的 Firebase 项目以及要设置的平台。

以下屏幕截图显示了您需要回答的提示。

  1. 选择要使用的项目。在这种情况下,请使用 flutterfire-ui-codelab 1359cdeb83204baa
  2. 选择您想使用的平台。在此 Codelab 中,您可以逐步为 Web、iOS 和 Android 配置适用于 Flutter 的 Firebase Authentication,但也可以将 Firebase 项目设置为使用所有选项。301c9534f594f472
  3. 此屏幕截图显示了此过程结束时的输出。如果您熟悉 Firebase,就会发现您不必在控制台中创建平台应用(例如,Android 应用),FlutterFire CLI 会为您代劳。12199a85ade30459

完成后,在文本编辑器中查看 Flutter 应用。FlutterFire CLI 已生成一个名为 firebase_options.dart 的新文件。此文件包含一个名为 FirebaseOptions 的类,该类具有静态变量,用于存储每个平台所需的 Firebase 配置。如果您在运行 flutterfire configure 时选择了所有平台,则会看到名为 webandroidiosmacos 的静态值。

firebase_options.dart

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

/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
///   options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
 static FirebaseOptions get currentPlatform {
   if (kIsWeb) {
     return web;
   }
   // ignore: missing_enum_constant_in_switch
   switch (defaultTargetPlatform) {
     case TargetPlatform.android:
       return android;
     case TargetPlatform.iOS:
       return ios;
     case TargetPlatform.macOS:
       return macos;
   }

   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.appspot.com',
   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.appspot.com',
 );

 static const FirebaseOptions ios = FirebaseOptions(
   apiKey: 'AIzaSyBqLWsqFjYAdGyihKTahMRDQMo0N6NVjAs',
   appId: '1:963656261848:ios:d9e01cfe8b675dfcb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   storageBucket: 'flutterfire-ui-codelab.appspot.com',
   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.appspot.com',
   iosClientId: '963656261848-v7r3vq1v6haupv0l1mdrmsf56ktnua60.apps.googleusercontent.com',
   iosBundleId: 'com.example.complete',
 );
}

Firebase 使用“应用”一词来指代 Firebase 项目中特定平台的特定构建。例如,名为 FlutterFire-ui-codelab 的 Firebase 项目有多个应用:一个用于 Android,一个用于 iOS,一个用于 MacOS,还有一个用于 Web。

DefaultFirebaseOptions.currentPlatform 方法使用 Flutter 提供的 TargetPlatform 枚举检测您的应用在哪个平台上运行,然后返回正确的 Firebase 应用所需的 Firebase 配置值。

将 Firebase 软件包添加到 Flutter 应用

最后一步是将相关的 Firebase 软件包添加到您的 Flutter 项目。firebase_options.dart 文件应该包含错误,因为它依赖于尚未添加的 Firebase 软件包。在终端中,确保您位于 Flutter 项目的根目录 flutter-codelabs/firebase-emulator-suite/start。然后,运行以下三个命令:

flutter pub add firebase_core
flutter pub add firebase_auth
flutter pub add firebase_ui_auth

此时,您只需要这些软件包。

初始化 Firebase

为了使用添加的软件包,并且 DefaultFirebaseOptions.currentPlatform, 需要更新 main.dart 文件的 main 函数中的代码,

main.dart

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


 runApp(const MyApp());
}

此代码会执行两项操作。

  1. WidgetsFlutterBinding.ensureInitialized() 用于告知 Flutter,在 Flutter 框架完全启动之前,不要开始运行应用 widget 代码。Firebase 使用原生平台渠道,需要框架才能运行。
  2. Firebase.initializeApp 会在您的 Flutter 应用与 Firebase 项目之间建立连接。DefaultFirebaseOptions.currentPlatform 是从我们生成的 firebase_options.dart 文件导入的。此静态值可检测您在哪个平台上运行,并传入相应的 Firebase 密钥。

4.添加初始 Firebase 界面身份验证页面

Firebase 身份验证界面提供用于表示应用中的整个屏幕的微件。这些屏幕处理应用中不同的身份验证流程,例如登录、注册、忘记密码、用户个人资料等。首先,请在您的应用中添加一个着陆页,作为主应用的身份验证保护程序。

Material 或 Cupertino 应用

FlutterFire 界面要求您的应用封装在 MaterialApp 或 CupertinoApp 中。根据您的选择,界面会自动反映 Material 或 Cupertino 微件的差异。对于此 Codelab,请使用 MaterialApp,它已在 app.dart 中添加到该应用。

app.dart

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

class MyApp extends StatelessWidget {
 const MyApp({super.key});
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     theme: ThemeData(
       primarySwatch: Colors.blue,
     ),
     home: const AuthGate(),
   );
 }
}

检查身份验证状态

您需要先确定用户当前是否已经过身份验证,然后才能显示登录屏幕。若要检查这一点,最常见的方法是使用 Firebase Auth 插件监听 FirebaseAuth 的 authStateChanges。

在上面的代码示例中,MaterialApp 在其 build 方法中构建一个 AuthGate widget。(这是一个自定义 widget,FlutterFire 界面不提供。)

该 widget 需要更新以包含 authStateChanges 流。

authStateChanges API 会返回包含当前用户(如果用户已登录)的 Stream,如果用户未登录,则返回 null。如需在我们的应用中订阅此状态,您可以使用 Flutter 的 StreamBuilder 微件并将流传递给它。

StreamBuilder 是一个 widget,它根据您向其传递的数据流中的数据的最新快照自行构建。当数据流发出新快照时,它会自动重新构建。

更新 auth_gate.dart 中的代码。

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});

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

        return const HomeScreen();
      },
    );
  }
}
  • 系统正在向 StreamBuilder.stream 传递上述数据流 FirebaseAuth.instance.authStateChanged,如果用户已通过身份验证,它将返回 Firebase User 对象。(否则,它将返回 null。)
  • 接下来,代码使用 snapshot.hasData 检查流中的值是否包含 User 对象。
  • 如果没有,它将返回 SignInScreen widget。目前,该屏幕不会执行任何操作。这会在下一步中更新。
  • 否则,它会返回 HomeScreen,这是应用的主要部分,只有通过身份验证的用户才能访问。

SignInScreen 是一个来自 FlutterFire 界面软件包的 widget。这将是此 Codelab 下一步的重点。此时,当您运行应用时,您应该会看到空白的登录屏幕。

5. 登录屏幕

FlutterFire 界面提供的 SignInScreen widget 添加了以下功能:

  • 允许用户登录
  • 如果用户忘记了密码,可以点按“忘记了密码?”,然后系统会将您转到一个表单以重置密码
  • 如果用户尚未注册,可以点按“注册”,系统会将您转到其他可以注册的表单。

同样,这只需要几行代码。回顾 AuthGate 微件中的代码:

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});

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

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

SignInScreen widget 及其 providers 参数是获得所有上述功能所需的唯一代码。现在,您应该会看到一个登录屏幕,其中包含“电子邮件”和“密码”文本输入以及“登录”按钮。

虽然可以正常使用,但缺少样式。该 widget 会提供参数以自定义登录屏幕的外观。例如,您可能想要添加贵公司的徽标。

自定义登录屏幕

标题构建器

使用 SignInScreen.headerBuilder 参数,您可以在登录表单上方添加所需的任何 widget。使用以下代码更新 auth_gate.dart 文件:

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});

 @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'),
               ),
             );
           },
         );
       }

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

headerBuilder 参数需要一个 HeaderBuilder 类型的函数,该函数在 FlutterFire 界面软件包中定义。

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

由于它是一个回调,因此它会公开您可以使用的值(例如 BuildContextBoxConstraints),并要求您返回 widget。您返回的微件会显示在屏幕顶部。在此示例中,新代码会在屏幕顶部添加一张图片。现在,您的应用应如下所示。

73d7548d91bbd2ab

字幕制作工具

登录屏幕公开了三个用于自定义屏幕的附加参数:subtitleBuilderfooterBuildersideBuilder

subtitleBuilder 略有不同,因为回调参数包含一个类型为 AuthAction 的操作。AuthAction 是一个枚举,可用于检测用户所在的屏幕是“登录”屏幕还是“注册”屏幕。

更新 auth_gate.dart 中的代码以使用 subtitleBuilder。

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});

 @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('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!'),
             );
           },
         );
       }

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

重新加载应用,它应如下所示

footerBuilder 参数与 subtitleBuilder 相同。它不会公开 BoxConstraintsshrinkOffset,因为它专用于文本而不是图片。(不过您可以添加任何所需的微件。)

使用此验证码向您的登录屏幕添加页脚。

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});

 @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('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),
               ),
             );
           },
         );
       }

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

侧边构建器

SignInScreen.sidebuilder 参数接受回调,这次该回调的参数为 BuildContextdouble shrinkOffset。sideBuilder 返回的 widget 将显示在登录表单的左侧,并且仅在宽屏上显示。实际上,这意味着 widget 只会显示在桌面设备和 Web 应用中。

在内部,FlutterFire 界面使用断点来确定标题内容应该显示(在移动设备等高屏幕上),还是应显示侧边内容(在宽屏、桌面设备或网页上)。具体来说,如果屏幕宽度超过 800 像素,则显示侧边构建器内容,而标头内容不显示。如果屏幕的宽度小于 800 像素,则相反。

更新 auth_gate.dart 中的代码以添加 SideBuilder widget。

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});

 @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('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 控制台中的“用户”表格。
  2. 点击此处
  3. 选择“flutterfire-ui-codelab”(如果您使用的是其他名称,请选择其他项目)。您会看到如下表格:

f038fd9a58ed60d9.png

  1. 点击“添加用户”按钮。

2d78390d4c5dbbfa

  1. 输入新用户的电子邮件地址和密码。这可能是虚假的电子邮件地址和密码,正如我在下图中输入的一样。问题是可以解决的,但如果您使用的是虚假电子邮件地址,“忘记了密码”功能将无法使用。

62ba0feb33d54add.png

  1. 点击“添加用户”

32b236b3ef94d4c7

现在,您可以返回 Flutter 应用,通过登录页面让用户登录。您的应用应如下所示:

dd43d260537f3b1a

6. 个人资料屏幕

FlutterFire 界面还提供了一个 ProfileScreen widget,它同样只需几行代码即可为您提供大量功能。

添加 ProfileScreen widget

在文本编辑器中,找到 home.dart 文件。请使用以下代码更新该对象:

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: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

值得注意的新代码是传递给 IconButton.isPressed method. 的回调。按下 IconButton 后,应用会创建一个新的匿名路由,并导航到该路由。该路线将显示从 MaterialPageRoute.builder 回调返回的 ProfileScreen widget。

重新加载您的应用,然后推送右上角的图标(在应用栏中),将显示如下页面:

36487fc4ab4f26a7

这是 FlutterFire 界面页面提供的标准界面。所有按钮和文本字段都连接到 Firebase Auth,开箱即可使用。例如,您可以在“Name”文本字段输入一个名称,FlutterFire 界面将调用 FirebaseAuth.instance.currentUser?.updateDisplayName 方法,该方法会将该名称保存在 Firebase 中。

退出账号

目前,如果您按“退出”按钮,该应用程序不会有任何变化。它会将您退出,但不会将您导航回 AuthGate 小部件。如需实现这一点,请使用 ProfileScreen.actions 参数。

首先,更新 home.dart 中的代码。

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: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

现在,当您创建 ProfileScreen 的实例时,还会向 ProfileScreen.actions 实参传递操作列表。这些操作的类型为 FlutterFireUiAction。有许多不同的类是 FlutterFireUiAction 的子类型,一般来说,您可以使用这些类告知应用对不同的身份验证状态变化做出响应。当 Firebase 身份验证状态更改为 currentUser 为 null 时,SignedOutAction 会调用您提供给它的回调函数。

通过添加在 SignedOutAction 触发时调用 Navigator.of(context).pop() 的回调,应用会转到上一页。在此示例应用中,只有一条永久路由,如果该路由在没有用户登录时显示登录页面,在有用户时显示首页。由于这会在用户退出登录后发生,因此应用会显示登录页面。

自定义档案页面

与登录页面类似,个人资料页面也可以自定义。首先,用户进入个人资料页后,当前页面无法再返回到首页。为 ProfileScreen widget 提供 AppBar 可解决此问题。

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: [
           Image.asset('dash.png'),
           Text(
             'Welcome!',
             style: Theme.of(context).textTheme.displaySmall,
           ),
           const SignOutButton(),
         ],
       ),
     ),
   );
 }
}

ProfileScreen.appBar 参数接受 Flutter Material 软件包中的 AppBar widget,因此可像处理您已构建并传递给 Scaffold 的任何其他 AppBar 一样处理该 widget。在此示例中,保留了自动添加“返回”按钮的默认功能,且屏幕现在有一个标题。

向个人资料屏幕添加儿童

ProfileScreen widget 还有一个名为 child 的可选参数。该参数接受 widget 列表,并且这些 widget 将垂直放置在已在内部用于构建 ProfileScreen 的 Column widget 内。ProfileScreen build 方法中的此列 widget 会将您传递的子级放置在“Sign out”按钮上方。

更新 home.dart 中的代码以在此处显示公司徽标,类似于登录屏幕。

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: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

重新加载您的应用,您会在屏幕上看到以下内容:

ebe5792b765dbf87

7. 多平台 Google 身份验证登录

FlutterFire 界面还提供用于向第三方提供方(如 Google、Twitter、Facebook、Apple 和 GitHub)进行身份验证的微件和功能。

要与 Google 身份验证集成,请安装官方 firebase_ui_oauth_google 插件及其依赖项,以便处理原生身份验证流程。在终端中,转到 Flutter 项目的根目录并输入以下命令:

flutter pub add google_sign_in
flutter pub add firebase_ui_oauth_google

启用 Google 登录提供方

接下来,在 Firebase 控制台中启用 Google 提供方:

  1. 前往控制台中的 Authentication 登录提供方屏幕。
  2. 点击“添加新提供商”。8286fb28be94bf30
  3. 选择“Google”。c4e28e6f4974be7f
  4. 切换标有“启用”的开关,然后按“保存”。e74ff86990763826
  5. 如果显示模态窗口,其中包含与下载配置文件相关的信息,请点击“完成”。
  6. 确认已添加 Google 登录提供方。5329ce0543c90d95

添加 Google 登录按钮

启用 Google 登录功能后,向登录页面添加显示风格化的 Google 登录按钮所需的微件。导航到 auth_gate.dart 文件并将代码更新为以下内容:

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'; // new
import 'package:flutter/material.dart';

import 'home.dart';

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

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider(),
             GoogleProvider(clientId: "YOUR_WEBCLIENT_ID"),  // new
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('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 widget 配置。

添加完成后,重新加载应用,您就会看到 Google 登录按钮。

aca71a46a011bfb5.png

配置登录按钮

如果没有其他配置,该按钮将不起作用。如果您使用 Flutter Web 进行开发,则只需添加此步骤,即可让 Flutter Web 发挥作用。其他平台需要执行额外的步骤,下文将对此进行详细介绍。

  1. 前往 Firebase 控制台中的“Authentication 提供方”页面。
  2. 点击 Google 提供商。9b3a325c5eca6e49.png
  3. 点击“Web SDK 配置”展开面板。
  4. 复制“Web 客户端 ID”711a79f0d931c60f 中的值
  5. 返回文本编辑器,然后将此 ID 传递给 clientId 具名形参,以更新 auth_gate.dart 文件中 GoogleProvider 的实例。
GoogleProvider(
   clientId: "YOUR_WEBCLIENT_ID"
)

输入 Web 客户端 ID 后,请重新加载您的应用。按下“使用 Google 账号登录”按钮后,系统会显示一个新窗口(如果您使用的是 Web),引导您完成 Google 登录流程。最初,代码如下所示:

14e73e3c9de704bb

配置 iOS

为了在 iOS 上实现此功能,还有一个额外的配置过程。

  1. 前往 Firebase 控制台中的“项目设置”屏幕。您会看到一张卡片,其中列出了您的 Firebase 应用,如下所示:fefa674acbf213cc.png
  2. 点击 iOS。请注意,您的应用名称与我的应用名称不同。如果您使用 flutter-codelabs/firebase-auth-flutterfire-ui/start 项目来按照此 Codelab 进行操作,则我会在其中显示“complete”(完成)的对应文字时将对应“start”。
  3. 点击“GoogleServices-Info.plist”按钮,下载所需的配置文件。f89b3192871dfbe3.png
  4. 将下载的文件拖放到名为 的目录中。/ios/Runner(位于 Flutter 项目中)。
  5. 从项目的根目录运行以下终端命令,打开 Xcode:

打开 ios/Runner.xcworkspace

  1. 右键点击 Runner 目录,然后选择“Add Files to "Runner”。858986063a4c5201
  2. 从文件管理器中选择 GoogleService-Info.plist。
  3. 返回到文本编辑器(不是 Xcode),将下面的 CFBundle网址Types 属性添加到 [my_project]/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 -->

如果您的 Flutter 应用已在 iOS 中运行,您必须完全将其关闭,然后重新运行。否则,在 iOS 中运行应用。

8. 恭喜!

您已完成“适用于 Flutter 的 Firebase Auth 界面”Codelab。您可以在 GitHub 上的“complete”目录中找到此 Codelab 完成后的代码:Flutter Codelab

所学内容

  • 设置 Flutter 应用以使用 Firebase
  • 在 Firebase 控制台中设置 Firebase 项目
  • FlutterFire CLI
  • Firebase CLI
  • 使用 Firebase Authentication
  • 使用 FlutterFire 界面在您的 Flutter 应用中轻松处理 Firebase 身份验证

后续步骤

了解详情

Sparky 与你一起庆祝!

2a0ad195769368b1.gif