在Flutter应用中实现Firebase认证

3,870 阅读8分钟

无论你是在做一个简单的待办事项应用,还是在建立你的梦想应用,认证都能帮助你用用户的特定信息来实现个性化体验。它也是隐私和安全的一个重要组成部分。

Firebase认证是一个预先配置好的后台服务,它使得使用SDK与移动应用的集成变得非常容易。你不需要为认证过程维护任何后端基础设施,而且Firebase支持与流行的身份供应商(如Google、Facebook和GitHub)的整合。

在本教程中,我们将向你展示如何将Firebase认证与你的Flutter应用集成。为了用一个实际的例子来证明,我们将引导你完成建立一个电子邮件-密码注册和登录的过程。

我们将走完以下步骤。

完成后的应用程序看起来会是这样的。

Final App Design

创建一个Flutter和Firebase项目

使用以下命令创建一个新的Flutter项目。

flutter create flutter_authentication

在你喜欢的代码编辑器中打开该项目。下面是如何用VS Code打开它。

code flutter_authentication

为了将Firebase与你的Flutter项目集成,你必须通过进入控制台创建一个新的Firebase项目。

添加一个新的项目并给它一个名字。在这个示例项目中,我们不需要谷歌分析,所以你可以禁用它。一旦你创建了你的项目,你会被引导到你的Firebase项目仪表板。

Flutter Authentication Page

为Android、iOS和Web设置Firebase

要在Android、iOS或Web上使用Firebase,你必须为每个平台完成一些配置。请看下面的完整配置指南。

现在我们有了使用Firebase的基本设置,让我们深入到我们的Flutter应用中去。

导入Firebase插件

在你开始实现认证逻辑之前,你需要导入以下插件。

  • [firebase_core](https://pub.dev/packages/firebase_core)在Flutter应用中使用任何Firebase服务,都需要使用Firebase认证。
  • [firebase_auth](https://pub.dev/packages/firebase_auth)以获得Firebase认证服务的访问权

将这些插件添加到你的pubspec.yaml 文件中。

dependencies:
  firebase_core: ^1.0.4
  firebase_auth: ^1.1.1

初始化Firebase应用程序

在Flutter应用中使用任何Firebase服务之前,你需要初始化Firebase App

main.dart 文件修改为以下内容。

import 'package:flutter/material.dart';
import 'screens/login_page.dart';
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Authentication',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.cyan,
      ),
      home: LoginPage(),
    );
  }
}

定义LoginPage

import 'package:flutter/material.dart';
class LoginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Firebase Authentication'),
      ),
    );
  }
}

添加一个新的方法来初始化Firebase App。

Future<FirebaseApp> _initializeFirebase() async {
    FirebaseApp firebaseApp = await Firebase.initializeApp();
    return firebaseApp;
}

因为这个方法是异步的,你必须在构建方法里面使用FutureBuilder

class LoginPage extends StatelessWidget {
  Future<FirebaseApp> _initializeFirebase() async {
    FirebaseApp firebaseApp = await Firebase.initializeApp();
    return firebaseApp;
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Firebase Authentication'),
      ),
      body: FutureBuilder(
        future: _initializeFirebase(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            return Column(
              children: [
                Text('Login'),
              ],
            );
          }
          return Center(
            child: CircularProgressIndicator(),
          );
        },
      ),
    );
  }
}

现在,我们只是在异步任务完成时显示一个简单的Text widget,其他的只是一个CircularProgressIndicator

注册一个新用户

当一个新用户到来时,在登录之前,他们必须注册到Firebase认证。

创建一个名为fire_auth.dart 的新dart文件,并定义一个名为registerUsingEmailPassword() 的新方法。

class FireAuth {
  static Future<User?> registerUsingEmailPassword({
    required String name,
    required String email,
    required String password,
  }) async {
    FirebaseAuth auth = FirebaseAuth.instance;
    User? user;
    try {
      UserCredential userCredential = await auth.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );
      user = userCredential.user;
      await user!.updateProfile(displayName: name);
      await user.reload();
      user = auth.currentUser;
    } on FirebaseAuthException catch (e) {
      if (e.code == 'weak-password') {
        print('The password provided is too weak.');
      } else if (e.code == 'email-already-in-use') {
        print('The account already exists for that email.');
      }
    } catch (e) {
      print(e);
    }
    return user;
  }
}

在这里,我们使用提供的电子邮件和密码注册一个新用户,并将用户的名字与这个文件联系起来。

可能会出现各种FirebaseAuthException 的错误,我们在上面的代码段中已经处理了这些错误。

用户签入和签出

要签入已经在我们的应用程序中注册的用户,请定义一个新的方法,称为signInUsingEmailPassword() ,传递用户的电子邮件和密码。

static Future<User?> signInUsingEmailPassword({
  required String email,
  required String password,
  required BuildContext context,
}) async {
  FirebaseAuth auth = FirebaseAuth.instance;
  User? user;

  try {
    UserCredential userCredential = await auth.signInWithEmailAndPassword(
      email: email,
      password: password,
    );
    user = userCredential.user;
  } on FirebaseAuthException catch (e) {
    if (e.code == 'user-not-found') {
      print('No user found for that email.');
    } else if (e.code == 'wrong-password') {
      print('Wrong password provided.');
    }
  }

  return user;
}

电子邮件和密码被用来生成Firebase提供的User 对象。这个User ,以后可以用来检索存储在该账户中的任何其他数据(例如,用户名、个人资料图片等)。

你可以使用signOut() 方法来注销一个用户。没有必要为注销创建一个单独的方法,因为它只是一行代码。

FirebaseAuth.instance.signOut();

发送电子邮件验证

假设你想在继续之前验证一个用户是否输入了正确的电子邮件地址。为了发送电子邮件验证,你可以使用User 对象上的sendEmailVerification() 方法。

user.sendEmailVerification();

刷新用户

我们将在FireAuth 类中再定义一个方法来刷新User

static Future<User?> refreshUser(User user) async {
  FirebaseAuth auth = FirebaseAuth.instance;

  await user.reload();
  User? refreshedUser = auth.currentUser;

  return refreshedUser;
}

定义验证器

我们的应用程序将有三个表单字段:姓名、电子邮件、密码。我们将为每个字段创建一个验证器。验证器将帮助检查用户是否在特定字段中输入了任何不合适的值,并显示相应的错误。

创建一个名为validator.dart 的新文件,定义一个类Validator ,并在其中指定三个方法(每个方法都将接受一个String 作为参数)。

  • validateName() ,检查姓名字段是否为空
  • validateEmail() ,检查电子邮件地址字段是否为空,并使用正则表达式验证其格式是否正确。
  • validatePassword() ,检查密码字段是否为空,并验证其长度是否超过六个字符。
class Validator {
  static String? validateName({required String name}) {
    if (name == null) {
      return null;
    }
    if (name.isEmpty) {
      return 'Name can\'t be empty';
    }

    return null;
  }

  static String? validateEmail({required String email}) {
    if (email == null) {
      return null;
    }
    RegExp emailRegExp = RegExp(
        r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$");

    if (email.isEmpty) {
      return 'Email can\'t be empty';
    } else if (!emailRegExp.hasMatch(email)) {
      return 'Enter a correct email';
    }

    return null;
  }

  static String? validatePassword({required String password}) {
    if (password == null) {
      return null;
    }
    if (password.isEmpty) {
      return 'Password can\'t be empty';
    } else if (password.length < 6) {
      return 'Enter a password with length at least 6';
    }

    return null;
  }
}

建立签到表格

让我们在LoginPage 中添加一个表单,用于接受用户的电子邮件地址和密码。

Login Page

定义一个GlobalKey

final _formKey = GlobalKey<FormState>();

添加一个表单并指定密钥。

Form(
  key: _formKey,
  child: Column(
    children: <Widget>[
      // Add widgets
    ],
  ),
)

接下来,添加两个TextFormField,以接受电子邮件和密码。

Form(
  key: _formKey,
  child: Column(
    children: <Widget>[
      TextFormField(
        controller: _emailTextController,
        focusNode: _focusEmail,
        validator: (value) => Validator.validateEmail(email: value),
      ),
      SizedBox(height: 8.0),
      TextFormField(
        controller: _passwordTextController,
        focusNode: _focusPassword,
        obscureText: true,
        validator: (value) => Validator.validatePassword(password: value),
      ),
    ],
  ),
)

Form 内添加两个按钮:一个用于登录,另一个用于导航到RegisterPage

Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Expanded(
      child: ElevatedButton(
        onPressed: () async {
          if (_formKey.currentState!.validate()) {
            User? user = await FireAuth.signInUsingEmailPassword(
              email: _emailTextController.text,
              password: _passwordTextController.text,
            );
            if (user != null) {
              Navigator.of(context)
                  .pushReplacement(
                MaterialPageRoute(builder: (context) => ProfilePage(user: user)),
              );
            }
          }
        },
        child: Text(
          'Sign In',
          style: TextStyle(color: Colors.white),
        ),
      ),
    ),
    Expanded(
      child: ElevatedButton(
        onPressed: () {
          Navigator.of(context).push(
            MaterialPageRoute(builder: (context) => RegisterPage()),
          );
        },
        child: Text(
          'Register',
          style: TextStyle(color: Colors.white),
        ),
      ),
    ),
  ],
)

登录按钮里面,我们调用了FireAuth.signInUsingEmailPassword() ,用于使用Firebase认证来执行登录过程。

RegisterPage 也将包含一个与此类似的Form ,只是多了一个字段,用于接受用户首次注册时的名字。

Register Page Blank

你可以在这里查看RegisterPage 的用户界面代码。

建立个人资料页面

ProfilePage ,我们将传递User 对象并显示以下细节:姓名、电子邮件和用户是否完成了电子邮件验证。

Register Page

这个页面还将包含两个按钮:一个用于发送电子邮件验证,另一个用于签出用户。

class ProfilePage extends StatefulWidget {
  final User user;
  const ProfilePage({required this.user});
  @override
  _ProfilePageState createState() => _ProfilePageState();
}
class _ProfilePageState extends State<ProfilePage> {
  bool _isSendingVerification = false;
  bool _isSigningOut = false;
  late User _currentUser;

  @override
  void initState() {
    _currentUser = widget.user;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Profile'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'NAME: ${_currentUser.displayName}',
              style: Theme.of(context).textTheme.bodyText1,
            ),
            SizedBox(height: 16.0),
            Text(
              'EMAIL: ${_currentUser.email}',
              style: Theme.of(context).textTheme.bodyText1,
            ),
            SizedBox(height: 16.0),
            _currentUser.emailVerified
                ? Text(
                    'Email verified',
                    style: Theme.of(context)
                        .textTheme
                        .bodyText1!
                        .copyWith(color: Colors.green),
                  )
                : Text(
                    'Email not verified',
                    style: Theme.of(context)
                        .textTheme
                        .bodyText1!
                        .copyWith(color: Colors.red),
                  ),
              // Add widgets for verifying email
              // and, signing out the user
          ],
        ),
      ),
    );
  }
}

发送电子邮件验证的按钮如下。

ElevatedButton(
  onPressed: () async {
    await _currentUser.sendEmailVerification();
  },
  child: Text('Verify email'),
)

我们还将添加一个IconButton ,当电子邮件被验证时,它可以用来刷新用户。

IconButton(
  icon: Icon(Icons.refresh),
  onPressed: () async {
    User? user = await FireAuth.refreshUser(_currentUser);
    if (user != null) {
      setState(() {
        _currentUser = user;
      });
    }
  },
)

最后是签出用户的按钮。

ElevatedButton(
  onPressed: () async {
    await FirebaseAuth.instance.signOut();

    Navigator.of(context).pushReplacement(
      MaterialPageRoute(
        builder: (context) => LoginPage(),
      ),
    );
  },
  child: Text('Sign out')
)

保持登录状态

还有一件重要的事情要做。在大多数应用程序中,你只需要登录一次,它就会在以后的访问中记住这个状态--也就是说,它会自动将你签入应用程序,这样你就不必每次都提供你的凭证。

_LoginPageState 类中,修改_initializeFirebase() 方法以检索当前用户。如果User 不是空的,这意味着用户已经登录到应用程序,所以只要用检索到的用户导航到UserInfoScreen

Future<FirebaseApp> _initializeFirebase() async {
    FirebaseApp firebaseApp = await Firebase.initializeApp();
    User? user = FirebaseAuth.instance.currentUser;
    if (user != null) {
      Navigator.of(context).pushReplacement(
        MaterialPageRoute(
          builder: (context) => ProfilePage(
            user: user,
          ),
        ),
      );
    }
    return firebaseApp;
}

结语

恭喜你!你已经成功地集成了Firebase认证。你已经成功地将Firebase认证与你的Flutter应用集成在一起。正如你可能已经注意到的,Firebase认证不仅提供了后台基础设施来轻松地认证用户,而且还提供了预定义的自动登录和电子邮件验证方法。而且还有很多值得探索的地方;Firebase认证还提供了对与一些身份提供者的整合的支持,包括谷歌、Facebook、Twitter、苹果等。

你可以在GitHub上找到示例项目中使用的代码。

如果你对这个Flutter和Firebase认证教程有任何建议或疑问,欢迎在TwitterLinkedIn上与我联系。

The postImplementing Firebase Authentication in a Flutter appappeared first onLogRocket Blog.