用Flutter进行电子邮件认证 - Firebase
认证是每个应用程序应该具备的基本功能之一。下面是如何使用Firebase Auth为您的Flutter应用添加认证的方法。
Firebase是Flutter应用程序的首选后端之一,因为它提供了许多免费的功能,以及与Flutter的良好整合。
Firebase提供的功能之一是认证。
因此,我们就可以在我们的应用程序中整合电子邮件、电话、谷歌、苹果和更多的认证。
为了让你快速了解文章的内容,我们将使用Firebase auth包进行Firebase认证。这篇文章是在Flutter 2.10版本上运行的一个例子中创建的!
那么,让我们开始进行基本的电子邮件/密码认证吧!
第0步:为auth创建一个私有包。
Flutter中的包是可以在项目间共享的代码库,它独立于项目,开发者将其纳入并重用,使工作变得简单而不费时。
为Firebase Auth创建一个包是一个非常好的选择,从而减少包对我们主项目的依赖。
因此,为了创建一个包,我们必须首先在我们的项目中创建一个名为packages的文件夹。然后,在这个位置,运行下面的命令。
flutter create --template=package auth_service
上述命令将在packages中创建一个名为auth_service的文件夹。
当你在终端运行这个命令时,你会发现某些文件被创建。
一旦这些文件被创建,你会在软件包中发现一个名为auth_service的文件夹。
第一步:从Firebase控制台启用认证功能并选择电子邮件/密码。
一旦你把你的项目和应用程序添加到Firebase控制台,前提步骤是在控制台的右侧面板上启用认证功能,并从中启用电子邮件/密码。
当你点击认证时,你会得到一个欢迎屏幕,在那里你可以点击开始。
当你点击该按钮时,你将被转到登录方式列表。从那里你可以启用电子邮件/密码。
第2步:为注册和登录创建一个简单的用户界面。
你可以用2个TextFields创建一个简单的用户界面,一个是电子邮件地址,一个是密码,还有一个按钮用来提交。
注意:使用StatelessWidget而不是StatefulWidget是一个很好的做法,因为它的重建成本比StatefulWidget低。
下面是一个例子。
login_view.dart
import 'package:auth_example/signup/view/signup_view.dart';
import 'package:auth_example/home/view/home_view.dart';
import 'package:flutter/material.dart';
class LoginView extends StatelessWidget {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Login'),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_LoginEmail(emailController: _emailController),
const SizedBox(height: 30.0),
_LoginPassword(passwordController: _passwordController),
const SizedBox(height: 30.0),
_SubmitButton(
email: _emailController.text,
password: _passwordController.text,
),
const SizedBox(height: 30.0),
_CreateAccountButton(),
],
),
),
);
}
}
class _LoginEmail extends StatelessWidget {
_LoginEmail({
Key? key,
required this.emailController,
}) : super(key: key);
final TextEditingController emailController;
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2,
child: TextField(
controller: emailController,
decoration: const InputDecoration(hintText: 'Email'),
),
);
}
}
class _LoginPassword extends StatelessWidget {
_LoginPassword({
Key? key,
required this.passwordController,
}) : super(key: key);
final TextEditingController passwordController;
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2,
child: TextField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
hintText: 'Password',
),
),
);
}
}
class _SubmitButton extends StatelessWidget {
_SubmitButton({
Key? key,
required this.email,
required this.password,
}) : super(key: key);
final String email, password;
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
print('hello');
},
child: const Text('Login'),
);
}
}
class _CreateAccountButton extends StatelessWidget {
const _CreateAccountButton({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return TextButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SignUpView(),
),
);
},
child: const Text('Create Account'),
);
}
}
signup_view.dart
import 'package:auth_example/home/view/home_view.dart';
import 'package:flutter/material.dart';
class SignUpView extends StatelessWidget {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Create Account'),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_CreateAccountEmail(emailController: _emailController),
const SizedBox(height: 30.0),
_CreateAccountPassword(passwordController: _passwordController),
const SizedBox(height: 30.0),
_SubmitButton(
email: _emailController.text,
password: _passwordController.text,
),
],
),
),
);
}
}
class _CreateAccountEmail extends StatelessWidget {
_CreateAccountEmail({
Key? key,
required this.emailController,
}) : super(key: key);
final TextEditingController emailController;
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2,
child: TextField(
controller: emailController,
decoration: const InputDecoration(hintText: 'Email'),
),
);
}
}
class _CreateAccountPassword extends StatelessWidget {
_CreateAccountPassword({
Key? key,
required this.passwordController,
}) : super(key: key);
final TextEditingController passwordController;
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2,
child: TextField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
hintText: 'Password',
),
),
);
}
}
class _SubmitButton extends StatelessWidget {
_SubmitButton({
Key? key,
required this.email,
required this.password,
}) : super(key: key);
final String email, password;
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
print('hello');
},
child: const Text('Create Account'),
);
}
}
正如你所看到的,这个用户界面是非常简单的。两个屏幕都有两个文本字段和一个按钮。当点击按钮时,我们只是添加了打印语句来打印用户提供的电子邮件和密码。
第3步:创建后端代码,将凭证传递给你的Firebase。
你需要做的第一件事是在你的pubspec.yaml文件(你创建的auth包)中添加2个包;firebase_core和firebase_auth。一旦完成,运行flutter pub get,这样Flutter框架就会将包的内容下载到你的本地系统。
现在,你需要在main.dart文件中的main()函数中初始化Firebase应用程序。
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
第1行,WidgetsFlutterBinding.sureInitialized();确保在我们的初始化没有完成之前,用户界面不会被渲染。
现在,让我们创建一个文件来包含我们的认证相关函数和Firebase调用。让我们在我们的auth包中把它命名为firebase_auth_service.dart。
下一步是创建两个函数:登录和注册。
Future<UserEntity?> signInWithEmailAndPassword({
required String email,
required String password,
}) async {
try {
final userCredential = await _firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
);
return _mapFirebaseUser(userCredential.user);
} on auth.FirebaseAuthException catch (e) {
throw _determineError(e);
}
}
Future<UserEntity?> createUserWithEmailAndPassword({
required String email,
required String password,
}) async {
try {
final userCredential = await _firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
return _mapFirebaseUser(_firebaseAuth.currentUser);
} on auth.FirebaseAuthException catch (e) {
throw _determineError(e);
}
}
所以,由于我们使用Firebase进行认证,我们有预先建立的方法来登录和注册。
对于注册,我们可以使用createUserWithEmailAndPassword,它需要两个参数,即电子邮件和密码。
同样地,我们可以使用signInWithEmailAndPassword,它也需要两个参数,即电子邮件和密码。
在这里,我们创建了两个用户定义的方法来处理我们的Firebase调用。这些方法将电子邮件和密码作为参数,并将它们传递给Firebase函数。
在这里,如果你注意到,我们正在返回UserEntity。我们刚刚创建了一个简单的模型类,它看起来像这样:
import 'package:equatable/equatable.dart';
class UserEntity extends Equatable {
const UserEntity({
required this.id,
required this.firstName,
required this.lastName,
required this.email,
required this.imageUrl,
});
final String id;
final String firstName;
final String lastName;
final String email;
final String imageUrl;
factory UserEntity.fromJson(Map<String, dynamic> json) => UserEntity(
id: json['id'] ?? "",
firstName: json['firstName'] ?? "",
lastName: json['lastName'] ?? "",
email: json['email'] ?? "",
imageUrl: json['imageUrl'] ?? "",
);
Map<String, dynamic> toJson() => <String, dynamic>{
'id': id,
'firstName': firstName,
'lastName': lastName,
'email': email,
'imageUrl': imageUrl,
};
factory UserEntity.empty() => const UserEntity(
id: "",
firstName: "",
lastName: "",
email: "",
imageUrl: "",
);
List<Object?> get props => [id, firstName, lastName, email, imageUrl];
}
我们还创建了一个名为_determineError的方法,它可以确定哪个错误被抛出。
处理异常总是一个很好的做法,这样我们的代码才不会被破坏 下面是它的代码。
AuthError _determineError(auth.FirebaseAuthException exception) {
switch (exception.code) {
case 'invalid-email':
return AuthError.invalidEmail;
case 'user-disabled':
return AuthError.userDisabled;
case 'user-not-found':
return AuthError.userNotFound;
case 'wrong-password':
return AuthError.wrongPassword;
case 'email-already-in-use':
case 'account-exists-with-different-credential':
return AuthError.emailAlreadyInUse;
case 'invalid-credential':
return AuthError.invalidCredential;
case 'operation-not-allowed':
return AuthError.operationNotAllowed;
case 'weak-password':
return AuthError.weakPassword;
case 'ERROR_MISSING_GOOGLE_AUTH_TOKEN':
default:
return AuthError.error;
}
}
}
这里,AuthError只是一个枚举。
enum AuthError {
invalidEmail,
userDisabled,
userNotFound,
wrongPassword,
emailAlreadyInUse,
invalidCredential,
operationNotAllowed,
weakPassword,
error,
}
现在,让我们从我们的用户界面中调用这些方法吧!
第4步:从用户界面调用函数!
下一步是在我们的用户界面中调用这些函数。
现在,要在我们的应用程序中使用我们创建的auth包,你可以把它添加到你的应用程序的pubspec.yaml中,然后在需要的地方导入它。
auth:
path: packages/auth
现在,对于我们的用户界面,目前我们只是在用户点击登录和注册的按钮时添加了打印语句。现在,是时候采取一些行动了!
onPressed: () async {
try {
await _authService.createUserWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text,
);
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => Home()));
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(e.toString()),
),
);
}
},
在这里,上面的代码是用于创建账户页面。在这里,我们调用了在auth_service.dart中创建的createUserWithEmailAndPassword,如果我们没有得到任何异常,我们就会导航到主屏幕,如果有任何错误就会显示SnackBar。
同样的方法,我们也可以对登录进行操作。
因此,你的最终代码将如下所示。
signup_view.dart
import 'package:auth_example/home/view/home_view.dart';
import 'package:auth_service/auth.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class SignUpView extends StatelessWidget {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Create Account'),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_CreateAccountEmail(emailController: _emailController),
const SizedBox(height: 30.0),
_CreateAccountPassword(passwordController: _passwordController),
const SizedBox(height: 30.0),
_SubmitButton(
email: _emailController.text,
password: _passwordController.text,
),
],
),
),
);
}
}
class _CreateAccountEmail extends StatelessWidget {
_CreateAccountEmail({
Key? key,
required this.emailController,
}) : super(key: key);
final TextEditingController emailController;
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2,
child: TextField(
controller: emailController,
decoration: const InputDecoration(hintText: 'Email'),
),
);
}
}
class _CreateAccountPassword extends StatelessWidget {
_CreateAccountPassword({
Key? key,
required this.passwordController,
}) : super(key: key);
final TextEditingController passwordController;
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2,
child: TextField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
hintText: 'Password',
),
),
);
}
}
class _SubmitButton extends StatelessWidget {
_SubmitButton({
Key? key,
required this.email,
required this.password,
}) : super(key: key);
final String email, password;
final AuthService _authService = FirebaseAuthService(
authService: FirebaseAuth.instance,
);
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
try {
await _authService.createUserWithEmailAndPassword(
email: email,
password: password,
);
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => Home(),
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(e.toString()),
),
);
}
},
child: const Text('Create Account'),
);
}
}
login_view.dart
import 'package:auth_example/signup/view/signup_view.dart';
import 'package:auth_example/home/view/home_view.dart';
import 'package:auth_service/auth.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class LoginView extends StatelessWidget {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Login'),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_LoginEmail(emailController: _emailController),
const SizedBox(height: 30.0),
_LoginPassword(passwordController: _passwordController),
const SizedBox(height: 30.0),
_SubmitButton(
email: _emailController.text,
password: _passwordController.text,
),
const SizedBox(height: 30.0),
_CreateAccountButton(),
],
),
),
);
}
}
class _LoginEmail extends StatelessWidget {
_LoginEmail({
Key? key,
required this.emailController,
}) : super(key: key);
final TextEditingController emailController;
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2,
child: TextField(
controller: emailController,
decoration: const InputDecoration(hintText: 'Email'),
),
);
}
}
class _LoginPassword extends StatelessWidget {
_LoginPassword({
Key? key,
required this.passwordController,
}) : super(key: key);
final TextEditingController passwordController;
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width / 2,
child: TextField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
hintText: 'Password',
),
),
);
}
}
class _SubmitButton extends StatelessWidget {
_SubmitButton({
Key? key,
required this.email,
required this.password,
}) : super(key: key);
final String email, password;
final AuthService _authService = FirebaseAuthService(
authService: FirebaseAuth.instance,
);
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
try {
await _authService.signInWithEmailAndPassword(
email: email,
password: password,
);
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) => Home()));
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(e.toString()),
),
);
}
},
child: const Text('Login'),
);
}
}
class _CreateAccountButton extends StatelessWidget {
const _CreateAccountButton({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return TextButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SignUpView(),
),
);
},
child: const Text('Create Account'),
);
}
}
所以,现在这样,我们可以在任何我们想使用的项目中使用auth_service包。
如果在未来的任何时候,我们想把后端从Firebase改为其他服务,我们只需要更新这个包,我们的任务就完成了 这使我们能够使我们的应用程序更具可扩展性!
所以,总结一下步骤:
- 在你的Firebase控制台中添加项目和应用程序。
- 从控制台启用认证和电子邮件/密码认证。
- 为认证服务创建一个包。
- 创建简单的用户界面来获取用户的电子邮件和密码。
- 从用户界面中调用这些函数
等等,你喜欢这篇文章吗?
这是一个适合初学者的解决方案,您可以轻松地将Flutter - Firebase纳入并实现有效的电子邮件认证。
然而,为了创建一个更具可扩展性和强大的产品,有必要使用一个状态管理解决方案。
如果你有兴趣,我们将分享关于如何建立一个高质量产品的见解。
所以,请继续关注我们的下一篇文章😉!