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

1. 开始之前

在此 Codelab 中,您将了解如何使用 FlutterFire UI 包将 Firebase 身份验证添加到 Flutter 应用。使用此软件包,您可以将电子邮件/密码身份验证和 Google 登录身份验证添加到 Flutter 应用程序中。您还将了解如何设置 Firebase 项目,以及如何使用 FlutterFire CLI 在 Flutter 应用中初始化 Firebase。

先决条件

此 Codelab 假设您有一定的 Flutter 经验。如果没有,您可能想先学习基础知识。以下链接很有帮助:

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

你将创造什么

此 Codelab 将指导您使用 Firebase 进行身份验证,构建 Flutter 应用的身份验证流程。该应用程序将有一个登录屏幕、一个“注册”屏幕、一个密码恢复屏幕和一个用户配置文件屏幕。

6604fc9157f2c6ae.pngeab9509a41074930.pngda49189a5838e0bb.pngb2ccfb3632b77878.png

你将学到什么

该代码实验室涵盖:

  • 将 Firebase 添加到 Flutter 应用
  • Firebase 控制台设置
  • 使用 Firebase CLI 将 Firebase 添加到您的应用程序
  • 使用 FlutterFire CLI 在 Dart 中生成 Firebase 配置
  • 将 Firebase 身份验证添加到您的 Flutter 应用程序
  • 控制台中的 Firebase 身份验证设置
  • 使用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,因为您不会为此应用程序使用 Analytics。

d1fcec48bf251eaa.png

要了解有关 Firebase 项目的更多信息,请参阅了解 Firebase 项目

您正在构建的应用使用Firebase 身份验证来允许您的用户登录您的应用。它还允许新用户从 Flutter 应用程序注册。

Firebase 身份验证需要使用 Firebase 控制台启用,并且启用后需要特殊配置。

启用电子邮件登录以进行 Firebase 身份验证

要允许用户登录 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

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

如果您想按照 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.png
  2. 选择您要使用的平台。在此 Codelab 中,提供了为 Web、iOS 和 Android 版 Flutter 配置 Firebase 身份验证的步骤,但您可以将 Firebase 项目设置为使用所有选项。 301c9534f594f472.png
  3. 此屏幕截图显示了该过程结束时的输出。如果您熟悉 Firebase,您会注意到您不必在控制台中创建平台应用程序(例如 Android 应用程序),FlutterFire CLI 已为您完成了这件事。 12199a85ade30459.png

完成后,在文本编辑器中查看 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函数中的代码。

主程序.dart

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


 runApp(const MyApp());
}

这段代码做了两件事。

  1. WidgetsFlutterBinding.ensureInitialized()告诉 Flutter 在 Flutter 框架完全启动之前不要开始运行应用程序小部件代码。 Firebase 使用本机平台通道,这需要框架正在运行。
  2. Firebase.initializeApp在您的 Flutter 应用程序和 Firebase 项目之间建立连接。 DefaultFirebaseOptions.currentPlatform是从我们生成的firebase_options.dart文件导入的。此静态值会检测您正在运行的平台,并传入相应的 Firebase 键。

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

Firebase UI for Auth 提供了代表应用程序中整个屏幕的小部件。这些屏幕处理整个应用程序中的不同身份验证流程,例如登录、注册、忘记密码、用户配置文件等。首先,向您的应用程序添加一个登录页面,充当主应用程序的身份验证防护。

Material 或 Cupertino 应用程序

FlutterFire UI 要求您的应用程序包装在 MaterialApp 或 CupertinoApp 中。根据您的选择,UI 将自动反映 Material 或 Cupertino 小部件的差异。对于此 Codelab,请使用MaterialApp ,它已添加到应用程序的app.dart中。

应用程序.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正在其构建方法中构建AuthGate小部件。 (这是一个自定义小部件,FlutterFire UI 不提供。)

该小部件需要更新以包含authStateChanges流。

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

StreamBuilder是一个小部件,它基于您传递给它的Stream的最新数据快照来构建自身。当 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小部件。目前,该屏幕不会执行任何操作。这将在下一步中更新。
  • 否则,它返回一个HomeScreen ,这是应用程序的主要部分,只有经过身份验证的用户才能访问。

SignInScreen是一个来自 FlutterFire UI 包的小部件。这将是本 Codelab 下一步的重点。此时运行应用程序时,您应该会看到一个空白的登录屏幕。

5. 登录屏幕

FlutterFire UI 提供的SignInScreen小部件添加了以下功能:

  • 允许用户登录
  • 如果用户忘记密码,可以点击“忘记密码?”并填写表格以重置密码
  • 如果用户尚未注册,他们可以点击“注册”,然后进入另一个允许他们注册的表单。

同样,这只需要几行代码。回想一下 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小部件及其providers参数是获取所有上述功能所需的唯一代码。您现在应该看到一个登录屏幕,其中包含“电子邮件”和“密码”文本输入以及“登录”按钮。

虽然功能齐全,但缺乏造型。该小部件公开参数来自定义登录屏幕的外观。例如,您可能想添加您公司的徽标。

自定义登录屏幕

标题生成器

使用SignInScreen.headerBuilder参数,您可以在登录表单上方添加所需的任何小部件。使用以下代码更新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 UI 包中定义。

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

因为它是一个回调,所以它公开了您可以使用的值,例如BuildContextBoxConstraints ,并要求您返回一个小部件。无论您返回哪个小部件,都会显示在屏幕顶部。在此示例中,新代码将图像添加到屏幕顶部。您的应用程序现在应该如下所示。

73d7548d91bbd2ab.png

字幕生成器

登录屏幕公开了三个附加参数,允许您自定义屏幕: 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 返回的小部件将显示在登录表单的左侧,并且仅在宽屏幕上显示。实际上,这意味着该小部件将仅显示在桌面和网络应用程序上。

在内部,FlutterFire UI 使用断点来确定是否应显示标题内容(在高屏幕上,如移动设备)或应显示侧面内容(在宽屏幕、桌面或 Web 上)。具体来说,如果屏幕宽度超过 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.png

创建用户

至此,该屏幕的所有代码都已完成。不过,在登录之前,您需要创建一个用户。您可以通过“注册”屏幕执行此操作,也可以在 Firebase 控制台中创建用户。

要使用控制台:

  1. 转到 Firebase 控制台中的“用户”表。
  2. 点击这里
  3. 选择“flutterfire-ui-codelab”(如果您使用了不同的名称,则选择另一个项目)。你会看到这个表:

f038fd9a58ed60d9.png

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

2d78390d4c5dbbfa.png

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

62ba0feb33d54add.png

  1. 点击“添加用户”

32b236b3ef94d4c7.png

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

dd43d260537f3b1a.png

6. 个人资料屏幕

FlutterFire UI 还提供了一个ProfileScreen小部件,它再次通过几行代码为您提供了许多功能。

添加个人资料屏幕小部件

在文本编辑器中导航到home.dart文件。使用以下代码更新它:

主页.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时,您的应用程序会创建一个新的匿名路由并导航到它。该路由将显示ProfileScreen小部件,该小部件是从MaterialPageRoute.builder回调返回的。

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

36487fc4ab4f26a7.png

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

退出

现在,如果您按“退出”按钮,应用程序将不会更改。它会将您注销,但不会导航回 AuthGate 小部件。要实现此目的,请使用 ProfileScreen.actions 参数。

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

主页.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 小部件提供一个 AppBar 来解决此问题。

主页.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小部件,因此可以像您构建并传递给Scaffold的任何其他AppBar一样对待它。在此示例中,保留了自动添加“后退”按钮的默认功能,并且屏幕现在具有标题。

将孩子添加到个人资料屏幕

ProfileScreen 小部件还有一个名为 Children 的可选参数。此参数接受小部件列表,这些小部件将垂直放置在已在内部用于构建 ProfileScreen 的 Column 小部件内部。 ProfileScreen 构建方法中的此 Column 小部件会将您传递给它的子项放置在“注销”按钮上方。

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

主页.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.png

7. 多平台 Google Auth 登录

FlutterFire UI 还提供了用于向第三方提供商(例如 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.png
  3. 选择“谷歌”。 c4e28e6f4974be7f.png
  4. 切换标记为“启用”的开关,然后按“保存”。 e74ff86990763826.png
  5. 如果出现包含有关下载配置文件的信息的模式,请单击“完成”。
  6. 确认已添加 Google 登录提供商。 5329ce0543c90d95.png

添加 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 小部件配置中。

添加完毕后,重新加载您的应用程序,您将看到一个 Google 登录按钮。

aca71a46a011bfb5.png

配置登录按钮

如果没有额外的配置,该按钮将无法工作。如果您使用 Flutter Web 进行开发,这是您需要添加的唯一步骤才能使其正常工作。其他平台需要额外的步骤,稍后将对此进行讨论。

  1. 导航到Firebase 控制台中的身份验证提供程序页面。
  2. 单击 Google 提供商。 9b3a325c5eca6e49.png
  3. 单击“Web SDK 配置”扩展面板。
  4. 复制“Web 客户端 ID”中的值711a79f0d931c60f.png
  5. 返回到文本编辑器,并通过将此 ID 传递给clientId命名参数来更新文件auth_gate.dart中的GoogleProvider实例。
GoogleProvider(
   clientId: "YOUR_WEBCLIENT_ID"
)

输入 Web 客户端 ID 后,重新加载您的应用程序。当您按下“使用 Google 登录”按钮时,将会出现一个新窗口(如果您使用的是网络),引导您完成 Google 登录流程。最初,它看起来像这样:

14e73e3c9de704bb.png

配置 iOS

为了使其在 iOS 上运行,需要一个额外的配置过程。

  1. 导航到Firebase 控制台中的“项目设置”屏幕。将有一张卡片列出您的 Firebase 应用程序,如下所示: fefa674acbf213cc.png
  2. 单击 iOS。请注意,您的应用程序名称将与我的不同。如果您使用flutter-codelabs/firebase-auth-flutterfire-ui/start项目来执行此 Codelab,那么我的版本会显示“完成”,而您的版本则会显示“开始”。
  3. 单击“GoogleServices-Info.plist”按钮下载所需的配置文件。 f89b3192871dfbe3.png
  4. 将下载的文件拖放到名为 的目录中。 Flutter 项目中的/ios/Runner
  5. 通过从项目的根目录运行以下终端命令来打开 Xcode:

打开 ios/Runner.xcworkspace

  1. 右键单击 Runner 目录并选择将文件添加到“Runner”。 858986063a4c5201.png
  2. 从文件管理器中选择 GoogleService-Info.plist。
  3. 返回文本编辑器(不是 Xcode),将下面的 CFBundleURLTypes 属性添加到 [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 UI Codelab 。您可以在 github 上的“完整”目录中找到此 Codelab 的完整代码: Flutter Codelabs

我们涵盖的内容

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

下一步

了解更多

Sparky在这里与您一起庆祝!

2a0ad195769368b1.gif