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

1.准备工作

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

前提条件

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

您还应该具有一定的 Firebase 经验,但如果您从未将 Firebase 添加到过 Flutter 项目,也没有关系。如果您不熟悉 Firebase 控制台,或者完全不熟悉 Firebase,请先访问以下链接:

您将创建的内容

此 Codelab 将指导您使用 Firebase 进行身份验证,为 Flutter 应用构建身份验证流程。该应用会显示登录屏幕,即“Register”密码恢复屏幕和用户个人资料屏幕。

6604fc9157f2c6ae eab9509a41074930 da49189a5838e0bb b2ccfb3632b77878

学习内容

此 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 的网络控制台中创建一个 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

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

如果您想快进,或者查看完成后的呈现效果,请在名为 complete 的目录中查找交叉引用。

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

安装 Firebase CLI

Firebase CLI 提供了用于管理 Firebase 项目的工具。FlutterFire CLI 需要使用 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 项目中特定平台的特定 build。例如,名为 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 身份验证插件监听 FirebaseAuth 的 authStateChanges。

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

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

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

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

更新 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 参数是实现上述所有功能所需的唯一代码。您现在应该会看到一个包含“email”的登录屏幕和“password”以及“登录”按钮按钮。

尽管它能正常运行,但缺少样式。该 widget 提供用于自定义登录屏幕外观的参数。例如,您可以添加公司的徽标。

自定义登录屏幕

headerBuilder

使用 SignInScreen.headerBuilder 参数,您可以在登录表单上方添加所需的任何 widget。此 widget 仅显示在较窄的屏幕(例如移动设备)上。在宽屏上,您可以使用 SignInScreen.sideBuilder,此 Codelab 稍后会对此进行讨论。

使用以下代码更新 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.png

字幕制作工具

登录屏幕公开了三个额外的参数,您可以使用这些参数来自定义屏幕:subtitleBuilderfooterBuildersideBuilder

subtitleBuilder 略有不同,因为回调参数包含一个操作,其类型为 AuthActionAuthAction 是一个枚举,可用于检测用户所在屏幕是否为“登录”屏幕或“register”屏幕。

更新 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();
     },
   );
 }}

Side Builder

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

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

更新 auth_gate.dart 中的代码以添加 sideBuilder 微件。

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

创建用户

此时,此界面的所有代码均已编写完毕。不过,您需要先创建一个用户,然后才能登录。为此,您可以使用“注册”也可以在 Firebase 控制台中创建用户。

如需使用控制台,请执行以下操作:

  1. 前往“用户”表格。
  2. 点击此处
  3. 选择“flutterfire-ui-codelab”(如果您使用的是其他名称,则为其他项目)。您会看到以下表格:

f038fd9a58ed60d9

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

2d78390d4c5dbbfa.png

  1. 输入新用户的电子邮件地址和密码。这可能是虚假的电子邮件地址和密码,如下图所示。可以,但“忘记了密码”如果您使用虚假电子邮件地址,“ ”功能将不起作用。

62ba0feb33d54add

  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,开箱即可使用。例如,您可以在“名称”字段输入名称文本字段,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 的子类型,一般来说,您可以使用这些类来指示应用响应不同的身份验证状态变化。SignedOutAction 会调用一个回调函数,您可以在 Firebase 身份验证状态变为 currentUser 为 null 时向其提供该函数。

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

自定义个人资料页面

与“登录页面”类似,个人资料页面也可以自定义。首先,用户进入个人资料页面后,就无法返回到当前页面。为 ProfileScreen widget 指定一个应用栏,可解决此问题。

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。在本示例中,默认功能是自动添加“back”按钮的保存位置,屏幕现在有一个标题。

将儿童添加到个人资料屏幕

ProfileScreen widget 还有一个名为 child 的可选参数。此参数接受 widget 列表,这些 widget 将垂直放置在已在内部用于构建 ProfileScreen 的 Column widget 内。ProfileScreen 构建方法中的这个 Column 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 Auth 登录

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. 转到控制台中的身份验证登录提供商屏幕。
  2. 点击“添加新的提供方”。8286fb28be94bf30
  3. 选择“Google”。c4e28e6f4974be7f
  4. 切换标有“启用”的开关,然后按“保存”。e74ff86990763826
  5. 如果出现一个模态窗口,其中包含有关如何下载配置文件的信息,请点击“完成”。
  6. 确认已添加 Google 登录提供方。5329ce0543c90d95

添加 Google 登录按钮

启用 Google 登录功能后,向登录页面添加显示风格化 Google 登录按钮所需的 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: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();
     },
   );
 }
}

此处的唯一新代码是向 SignInScreen widget 配置添加 GoogleProvider(clientId: "YOUR_WEBCLIENT_ID")

完成上述操作后,重新加载您的应用,您就会看到 Google 登录按钮。

aca71a46a011bfb5

配置登录按钮

如果不进行其他配置,该按钮将不起作用。如果您使用 Flutter Web 进行开发,只需添加此步骤即可实现此功能。其他平台需要执行额外的步骤,我们稍后会讨论这些步骤。

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

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

14e73e3c9de704bb

配置 iOS

为使它在 iOS 上发挥作用,还需要执行一个额外的配置流程。

  1. 进入 Firebase 控制台中的“项目设置”屏幕。您会看到一张卡片,其中列出了您的 Firebase 应用,如下所示:fefa674acbf213cc.png
  2. 点击 iOS。请注意,您的应用名称与我的应用名称不同。我的问题是说“完成”如果您使用 flutter-codelabs/firebase-auth-flutterfire-ui/start 项目来按照此 Codelab 的说明操作,则您的 ID 将是“start”。
  3. 点击“GoogleServices-Info.plist”按钮下载所需的配置文件。F89b3192871dfbe3.png
  4. 将下载的文件拖放到名为 的目录中。/ios/Runner
  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。您可以在“complete”(完成)中找到此 Codelab 的完整代码GitHub 上的目录:Flutter Codelab

所学内容

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

后续步骤

了解详情

Sparky 在这里与您一起庆祝!

2a0ad195769368b1.gif