1. 准备工作
在此 Codelab 中,您将学习如何使用 FlutterFire 界面软件包将 Firebase Authentication 添加到 Flutter 应用。借助此软件包,您将向 Flutter 应用添加电子邮件和密码身份验证以及 Google 登录身份验证。您还将学习如何设置 Firebase 项目,以及如何使用 FlutterFire CLI 在 Flutter 应用中初始化 Firebase。
前提条件
此 Codelab 假定您具有一些 Flutter 经验。如果没有,您可能需要先了解基础知识。以下链接非常有用:
您还应具备一定的 Firebase 经验,但即使您从未将 Firebase 添加到 Flutter 项目中,也没关系。如果您不熟悉 Firebase 控制台,或者完全不了解 Firebase,请先参阅以下链接:
您将创建的内容
此 Codelab 将指导您使用 Firebase 构建 Flutter 应用的身份验证流程。该应用将包含登录界面、“注册”界面、密码恢复界面和用户个人资料界面。
学习内容
此 Codelab 涵盖以下内容:
- 将 Firebase 添加到 Flutter 应用
- Firebase 控制台设置
- 使用 Firebase CLI 将 Firebase 添加到您的应用
- 使用 FlutterFire CLI 在 Dart 中生成 Firebase 配置
- 向 Flutter 应用添加 Firebase Authentication
- 在控制台中设置 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 项目
- 使用您的 Google 账号登录 Firebase 控制台。
- 点击相应按钮以创建新项目,然后输入项目名称(例如
FlutterFire-UI-Codelab
)。
- 点击继续。
- 如果看到相关提示,请查看并接受 Firebase 条款,然后点击继续。
- (可选)在 Firebase 控制台中启用 AI 辅助功能(称为“Gemini in Firebase”)。
- 在此 Codelab 中,您不需要使用 Google Analytics,因此请关闭 Google Analytics 选项。
- 点击创建项目,等待项目完成预配,然后点击继续。
如需详细了解 Firebase 项目,请参阅了解 Firebase 项目。
为 Firebase Authentication 启用电子邮件登录
您正在构建的应用使用 Firebase Authentication,以便用户登录您的应用。它还允许新用户通过 Flutter 应用进行注册。
Firebase Authentication 需要使用 Firebase 控制台启用,并且在启用后需要进行特殊配置。
如需允许用户登录 Web 应用,您将首先使用电子邮件地址/密码登录方法。稍后,您将添加 Google 登录方法。
- 在 Firebase 控制台中,展开左侧面板中的构建菜单。
- 点击身份验证,然后点击开始使用按钮,接着点击登录方法标签页(或直接前往登录方法标签页)。
- 在登录提供方列表中,点击电子邮件地址/密码,将启用开关设置到开启位置,然后点击保存。
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
,另一个称为 start
。start
目录包含一个不完整的项目,您将在此目录中花费大部分时间。
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。如需查看适用于您操作系统的所有可用选项,请访问 firebase.google.com/docs/cli。
安装 CLI 后,您必须通过 Firebase 进行身份验证。
- 运行以下命令,使用您的 Google 账号登录 Firebase:
firebase login
- 此命令会将您的本地机器与 Firebase 关联,并授予您对 Firebase 项目的访问权限。
- 通过列出 Firebase 项目来测试 CLI 是否已正确安装,以及是否能访问您的账号。运行以下命令:
firebase projects:list
- 显示的列表应与 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 项目以及要设置的平台。
以下屏幕截图显示了您需要回答的提示。
- 选择要使用的项目。在这种情况下,请使用
flutterfire-ui-codelab
- 选择要使用的平台。在此 Codelab 中,我们提供了针对 Web、iOS 和 Android 为 Flutter 配置 Firebase Authentication 的步骤,但您可以设置 Firebase 项目以使用所有选项。
- 此屏幕截图显示了流程结束时的输出。如果您熟悉 Firebase,就会发现您不必在控制台中创建平台应用(例如 Android 应用),FlutterFire CLI 会为您完成此操作。
完成后,在文本编辑器中查看 Flutter 应用。FlutterFire CLI 已修改名为 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 项目中特定平台上的特定 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 firebase_auth firebase_ui_auth
这些是您目前唯一需要的软件包。
初始化 Firebase
为了使用添加的软件包,请更新 main.dart
文件中 main
函数的代码。DefaultFirebaseOptions.currentPlatform,
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));
}
此代码会执行两项操作。
WidgetsFlutterBinding.ensureInitialized()
会告知 Flutter 在 Flutter 框架完全启动之前不要开始运行应用 widget 代码。Firebase 使用原生平台渠道,这需要框架处于运行状态。Firebase.initializeApp
用于在 Flutter 应用和 Firebase 项目之间建立连接。DefaultFirebaseOptions.currentPlatform
是从我们生成的firebase_options.dart
文件中导入的。此静态值可检测您正在运行的平台,并传入相应的 Firebase 密钥。
4. 添加初始 Firebase 界面身份验证页面
Firebase Auth 的界面提供了一些 widget,这些 widget 代表应用中的整个屏幕。这些界面可处理整个应用中的不同身份验证流程,例如登录、注册、忘记密码、用户个人资料等。首先,向您的应用添加一个充当主应用身份验证保护机制的着陆页。
Material 应用或 Cupertino 应用
FlutterFire 界面要求您的应用封装在 MaterialApp
或 CupertinoApp
中。根据您的选择,界面会自动反映 Material 或 Cupertino widget 的差异。在此 Codelab 中,您将使用 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),
);
}
}
检查身份验证状态
在显示登录界面之前,您需要确定用户是否已通过身份验证。检查此状态的最常见方法是使用 Firebase Auth 插件监听 FirebaseAuth
的 authStateChanges
。
在上面的代码示例中,MaterialApp
在其 build
方法中构建 AuthGate
widget。(这是一个自定义 widget,并非由 FlutterFire 界面提供。)
该 widget 需要更新,以包含 authStateChanges
流。
authStateChanges
API 会返回一个 Stream
,其中包含当前用户(如果用户已登录),否则返回 null。如需在应用中订阅此状态,您可以使用 Flutter 的 StreamBuilder widget 并将该流传递给它。
StreamBuilder
是一个根据您传递给它的 Stream 中的最新数据快照自行构建的 widget。当 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
(即上述流),如果用户已通过身份验证,该流将返回 FirebaseUser
对象,否则将返回null
。- 接下来,代码使用
snapshot.hasData
检查来自流的值是否包含User
对象。 - 如果没有,则返回
SignInScreen
widget。目前,此屏幕不会执行任何操作,我们将在下一步中对其进行更新。 - 否则,它会返回
HomeScreen
,这是应用的主要部分,只有经过身份验证的用户才能访问。
SignInScreen
是一个来自 FlutterFire 界面软件包的 widget。这正是本 Codelab 下一步的重点。此时运行应用,您应该会看到空白的登录界面。
5. 登录界面
FlutterFire UI 提供的 SignInScreen
widget 添加了以下功能:
- 允许用户登录
- 如果用户忘记了密码,可以点按“忘记了密码?”,然后系统会显示一个表单,供用户重置密码
- 如果用户尚未注册,可以点按“注册”,然后系统会将其转到另一个表单,以便其进行注册。
同样,这只需要几行代码。回忆一下 AuthGate
widget 中的代码:
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
widget 及其 providers
实参是实现上述所有功能所需的唯一代码。您现在应该会看到一个登录界面,其中包含“电子邮件”和“密码”文本输入框,以及一个“登录”按钮。
虽然功能正常,但缺少样式。该 widget 会公开一些参数,用于自定义登录界面的外观。例如,您可能想要添加公司徽标。
自定义登录界面
headerBuilder
使用 SignInScreen.headerBuilder
实参,您可以在登录表单上方添加所需的任何 widget。此 widget 仅显示在窄屏幕上,例如移动设备。在宽屏上,您可以使用 SignInScreen.sideBuilder
,我们将在本 Codelab 的后面部分讨论该属性。
使用以下代码更新 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
),并要求您返回 widget。无论您返回哪个 widget,都会显示在屏幕顶部。在此示例中,新代码会在屏幕顶部添加一张图片。您的应用现在应如下所示。
字幕生成器
登录界面公开了三个额外的参数,可用于自定义界面: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
,因为它是为文本而非图片设计的。当然,您可以添加任何所需的 widget。
使用此代码向登录界面添加页脚。
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();
},
);
}
}
Side Builder
SignInScreen.sidebuilder 实参接受一个回调,这次该回调的实参为 BuildContext
和 double shrinkOffset
。sideBuilder
返回的 widget 将显示在登录表单的左侧,并且仅在宽屏幕上显示。实际上,这意味着该 widget 将仅在桌面设备和 Web 应用上显示。
在内部,FlutterFire 界面使用断点来确定是应显示标题内容(在手机等高屏幕上),还是应显示侧边内容(在桌面设备或网页等宽屏幕上)。具体来说,如果屏幕宽度超过 800 像素,则会显示侧边构建器内容,而不会显示标题内容。如果屏幕宽度小于 800 像素,则情况正好相反。
更新 auth_gate.dart 中的代码以添加 sideBuilder
widget。
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)。
创建用户
至此,此屏幕的所有代码均已完成。不过,在登录之前,您需要先创建用户。您可以通过“注册”界面执行此操作,也可以在 Firebase 控制台中创建用户。
如需使用控制台,请执行以下操作:
- 前往 Firebase 控制台中的“用户”表格。选择“flutterfire-ui-codelab”,如果您使用了其他名称,请选择相应项目。您会看到以下表格:
- 点击“添加用户”按钮。
- 为新用户输入电子邮件地址和密码。这可以是虚假的电子邮件地址和密码,如我在下图中输入的那样。这样做可以,但如果您使用虚假的电子邮件地址,“忘记密码”功能将无法正常运行。
- 点击“添加用户”图标
现在,您可以返回到 Flutter 应用,并使用登录页面登录用户。应用应如下所示:
6. 个人资料界面
FlutterFire UI 还提供了一个 ProfileScreen
widget,同样只需几行代码即可实现许多功能。
添加“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
时,您的应用会创建一个新的匿名路线并导航到该路线。该路由将显示从 MaterialPageRoute.builder
回调返回的 ProfileScreen
widget。
重新加载应用,然后按右上角(应用栏中)的图标,系统会显示如下页面:
这是 FlutterFire 界面页面提供的标准界面。所有按钮和文本字段都已连接到 Firebase Auth,并且开箱即可使用。例如,您可以在“名称”文本字段中输入一个名称,然后 FlutterFire 界面会调用 FirebaseAuth.instance.currentUser?.updateDisplayName
方法,该方法会将该名称保存在 Firebase 中。
退出
目前,如果您按“退出”按钮,应用不会发生任何变化。系统会退出您的账号,但不会将您重新导航到 AuthGate widget。如需实现此功能,请使用 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
的子类型,您通常使用它们来告知应用对不同的身份验证状态变化做出反应。当 Firebase 身份验证状态更改为 currentUser 为 null 时,SignedOutAction 会调用您为其提供的回调函数。
通过添加在 SignedOutAction
触发时调用 Navigator.of(context).pop()
的回调,应用将导航到上一页。在此示例应用中,只有一个永久性路线,即在没有用户登录时显示“登录”界面,在有用户登录时显示首页。由于这种情况发生在用户退出账号时,应用将显示“登录”界面。
自定义个人资料页面
与登录界面类似,个人资料页面也是可自定义的。首先,我们当前的网页在用户进入个人资料页面后,无法返回首页。通过为 ProfileScreen widget 提供 AppBar 来修复此问题。
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
实参接受来自 Flutter Material 软件包的 AppBar
widget,因此可以像您构建并传递给 Scaffold
的任何其他 AppBar
一样处理。在此示例中,系统保留了自动添加“返回”按钮的默认功能,并且屏幕现在有了标题。
向个人资料界面添加了“儿童”
ProfileScreen
widget 还有一个名为 children 的可选实参。此实参接受一个 widget 列表,这些 widget 将垂直放置在已在内部用于构建 ProfileScreen
的 Column
widget 内。ProfileScreen
build 方法中的此 Column
widget 会将您传递给它的子级放置在“退出”按钮上方。
更新 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(),
],
),
),
);
}
}
重新加载应用,您会在屏幕上看到以下内容:
7. 多平台 Google Auth 登录
FlutterFire 界面还提供用于通过 Google、Twitter、Facebook、Apple 和 GitHub 等第三方提供商进行身份验证的 widget 和功能。
如需与 Google 身份验证集成,请安装官方 firebase_ui_oauth_google 插件及其依赖项,这些插件将处理原生身份验证流程。在终端中,前往 Flutter 项目的根目录,然后输入以下命令:
flutter pub add google_sign_in firebase_ui_oauth_google
启用 Google 登录提供方
接下来,在 Firebase 控制台中启用 Google 提供方:
- 在控制台中前往身份验证登录提供商界面。
- 点击“添加新提供商”。
- 选择“Google”。
- 切换标有“启用”的开关,然后按“保存”。
- 如果系统显示一个模态框,其中包含有关下载配置文件的信息,请点击“完成”。
- 确认已添加 Google 登录提供方。
添加 Google 登录按钮
启用 Google 登录后,添加在登录屏幕上显示样式化 Google 登录按钮所需的 widget。前往 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
widget 配置中。
添加完成后,重新加载应用,您会看到一个 Google 登录按钮。
配置登录按钮
如果不进行额外配置,该按钮将无法正常运行。如果您使用 Flutter Web 进行开发,则只需添加此步骤即可使该功能正常运行。其他平台需要执行额外的步骤,我们稍后会介绍。
- 前往 Firebase 控制台中的“身份验证提供商”页面。
- 点击 Google 提供商。
- 点击“Web SDK 配置”展开面板。
- 复制“Web 客户端 ID”中的值。
- 返回到文本编辑器,并通过将此 ID 传递给名为
clientId
的参数来更新文件auth_gate.dart
中的GoogleProvider
实例。
GoogleProvider(
clientId: "YOUR_WEBCLIENT_ID"
)
输入 Web 客户端 ID 后,重新加载应用。如果您使用的是 Web,当您按“使用 Google 账号登录”按钮时,系统会显示一个新窗口,引导您完成 Google 登录流程。最初,它看起来像这样:
配置 iOS
为了让此功能在 iOS 上正常运行,还需要完成额外的配置流程。
- 在 Firebase 控制台中,前往“项目设置”界面。系统会显示一张列出您的 Firebase 应用的卡片,如下所示:
- 选择 iOS。请注意,您的应用名称将与屏幕截图中显示的名称不同。如果使用
flutter-codelabs/firebase-auth-flutterfire-ui/start
项目来完成此 Codelab,那么屏幕截图中显示“complete”的位置将显示“start”。 - 点击显示
GoogleServices-Info.plist
的按钮,下载所需的配置文件。 - 将下载的文件拖动到 Flutter 项目中名为
/ios/Runner
的目录。 - 在项目根目录中运行以下终端命令,打开 Xcode:
open ios/Runner.xcworkspace
- 右键点击 Runner 目录,然后选择“Add Files to "Runner"”(向“Runner”添加文件)。
- 从文件管理器中选择
GoogleService-Info.plist
。 - 返回到文本编辑器(不是 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 -->
- 您需要将在网页设置中添加的
GoogleProvider.clientId
替换为与 Firebase iOS 客户端 ID 关联的客户端 ID。首先,您可以在firebase_options.dart
文件中找到此 ID,它是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', );
- 将该值粘贴到
lib/main.dart
文件中的clientId
变量中。
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 Auth 界面(适用于 Flutter)Codelab。您可以在 GitHub 上的 firebase-auth-flutterfire-ui/complete
目录中找到此 Codelab 的完整代码。
所学内容
- 设置 Flutter 应用以使用 Firebase
- 在 Firebase 控制台中设置 Firebase 项目
- FlutterFire CLI
- Firebase CLI
- 使用 Firebase Authentication
- 使用 FlutterFire UI 在 Flutter 应用中处理 Firebase 身份验证
后续步骤
- 详细了解如何在 Flutter 中使用 Firestore 和 Authentication:了解如何将 Firebase 用于 Flutter Codelab
- 探索其他可用于构建 Flutter 应用的 Firebase 工具:
了解详情
- Firebase 网站:firebase.google.com
- Flutter 网站:flutter.dev
- FlutterFire Firebase Flutter widget:firebase.flutter.dev
- Firebase YouTube 频道
- Flutter YouTube 频道
Sparky 来与您一起庆祝了!