使用Flutter和Firebase的简单认证流程

390 阅读5分钟

用户认证是很多应用程序的一个非常普遍的要求。

在这篇文章中,我们用不到100行的代码在Flutter中实现了一个简单的认证流程。

作为其中的一部分,我们将看到如何。

这是我在GitHub上发表的《使用Flutter和Firebase的参考认证流程》的基础。

所以,让我们从最基本的开始。

初始设置

我们将在这个例子中使用Firebase认证。

在创建一个新的Flutter项目后,我们可以将 firebase_auth到我们的pubspec.yaml 文件的依赖项部分。

// pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  firebase_auth: 0.11.1+3

然后,我们需要配置我们的Flutter应用来使用Firebase。本指南将逐步解释该怎么做。

最重要的两个步骤是。

  • 在iOS和Android项目中添加GoogleServices-info.plistgoogle-services.json ,否则应用程序会在启动时崩溃。
  • 在Firebase控制台中启用匿名登录,因为我们将在这个例子中使用它。

我在我的Flutter & Firebase课程中详细介绍了所有这些步骤。

让我们编写代码

我们的应用程序将有两个页面,叫做SignInPageHomePage ,它们都是无状态的部件。

然后我们将有另一个widget叫做LandingPage 。我们将用它来决定根据用户的认证状态来显示哪个页面。

这里是这个应用程序的整个widget树。

让我们在代码中实现这一点。

SignInPage

首先,SignInPage

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';

class SignInPage extends StatelessWidget {

  Future<void> _signInAnonymously() async {
    try {
      await FirebaseAuth.instance.signInAnonymously();
    } catch (e) {
      print(e); // TODO: show dialog with error
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Sign in')),
      body: Center(
        child: RaisedButton(
          child: Text('Sign in anonymously'),
          onPressed: _signInAnonymously,
        ),
      ),
    );
  }
}

这个所做的就是显示一个居中的RaisedButton ,当按下这个按钮时就会调用_signInAnonymously()

这个方法调用FirebaseAuth.instance.signInAnonymously() ,并等待结果。

NOTES

  • try/catch 是用来捕捉任何异常的。如果登录失败,我们可以用它来提醒用户。
  • await FirebaseAuth.instance.signInAnonymously() 返回一个 ,但是我们的代码并没有使用这个返回值。这是因为我们将在其他地方处理用户的认证状态。FirebaseUser

说到这一点...

LandingPage

我们使用这个widget类来决定显示哪个页面。

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class LandingPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<FirebaseUser>(
      stream: FirebaseAuth.instance.onAuthStateChanged,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.active) {
          FirebaseUser user = snapshot.data;
          if (user == null) {
            return SignInPage();
          }
          return HomePage();
        } else {
          return Scaffold(
            body: Center(
              child: CircularProgressIndicator(),
            ),
          );
        }
      },
    );
  }
}

这个页面使用两个主要成分。

  • FirebaseAuth.instance.onAuthStateChanged 流。每次用户签入或签出时,它都会收到一个新的值。
  • 一个类型为FirebaseUserStreamBuilder 。它把onAuthStateChanged 作为一个输入流,并在流更新时调用builder

因此,当对FirebaseAuth.instance.signInAnonymously() 的调用成功时,一个新的FirebaseUser 被添加到onAuthStateChanged

结果,构建器被调用,我们可以从snapshot.data 中提取FirebaseUser 。而我们用这个来决定显示哪一个页面。

FirebaseUser user = snapshot.data;
if (user == null) {
  return SignInPage();
}
return HomePage();

还要注意我们是如何检查快照的connectionState

if (snapshot.connectionState == ConnectionState.active) {
  // do something
}

这可以是四个可能的值中的任何一个。none,waiting,active,done

当应用程序启动时,构建器首先被调用,ConnectionState.waiting 。我们可以用它来显示一个居中的CircularProgressIndicator()

一旦确定了认证状态,connectionState 变成active ,我们的构建器就会再次被调用。

综上所述,我们有三种可能的认证状态。

  • 未知
  • 用户已登录
  • 未签入的用户

而这段代码就是我们处理它们所需要的全部。

if (snapshot.connectionState == ConnectionState.active) {
  FirebaseUser user = snapshot.data;
  if (user == null) {
    return SignInPage();
  }
  return HomePage();
} else {
  return Scaffold(
    body: Center(
      child: CircularProgressIndicator(),
    ),
  );
}

继续前进...

主页

这个类类似于SignInPage

import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {

  Future<void> _signOut() async {
    try {
      await FirebaseAuth.instance.signOut();
    } catch (e) {
      print(e); // TODO: show dialog with error
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Page'),
        actions: <Widget>[
          FlatButton(
            child: Text(
              'Logout',
              style: TextStyle(
                fontSize: 18.0,
                color: Colors.white,
              ),
            ),
            onPressed: _signOut,
          ),
        ],
      ),
    );
  }
}

当注销按钮被按下时,这段代码调用FirebaseAuth.instance.signOut()

在成功时,一个null 值被添加到onAuthStateChanged 。因此,我们的LandingPage 中的构建器再次被调用,这次我们返回一个SignInPage()

完成了

差不多了。现在我们只需要更新我们的main.dart 文件,将LandingPage() 传递给MaterialApphome 参数。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.indigo,
      ),
      home: LandingPage(),
    );
  }
}

总而言之,整个流程只需要不到100行代码。

下面是整个例子的代码。

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.indigo,
      ),
      home: LandingPage(),
    );
  }
}

class LandingPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<FirebaseUser>(
      stream: FirebaseAuth.instance.onAuthStateChanged,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.active) {
          FirebaseUser user = snapshot.data;
          if (user == null) {
            return SignInPage();
          }
          return HomePage();
        } else {
          return Scaffold(
            body: Center(
              child: CircularProgressIndicator(),
            ),
          );
        }
      },
    );
  }
}

class SignInPage extends StatelessWidget {

  Future<void> _signInAnonymously() async {
    try {
      await FirebaseAuth.instance.signInAnonymously();
    } catch (e) {
      print(e); // TODO: show dialog with error
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Sign in')),
      body: Center(
        child: RaisedButton(
          child: Text('Sign in anonymously'),
          onPressed: _signInAnonymously,
        ),
      ),
    );
  }
}

class HomePage extends StatelessWidget {

  Future<void> _signOut() async {
    try {
      await FirebaseAuth.instance.signOut();
    } catch (e) {
      print(e); // TODO: show dialog with error
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Page'),
        actions: <Widget>[
          FlatButton(
            child: Text(
              'Logout',
              style: TextStyle(
                fontSize: 18.0,
                color: Colors.white,
              ),
            ),
            onPressed: _signOut,
          ),
        ],
      ),
    );
  }
}

这只用了一个main.dart 文件。我建议在你自己的项目中把widget类放在单独的文件中;)

结语

我们已经看到了如何用Firebase建立一个简单的认证流程。

这个例子没有使用任何花哨的应用程序架构。

有时候,保持简单的东西是一个好主意。正如阿尔伯特-爱因斯坦曾经说过的。

一切都应该尽可能的简单,但不能更简单

然而,爱因斯坦并不是一个软件开发者。😄

而我提出的代码有两个主要缺点。

1)全球访问

LandingPage,SignInPage,HomePage 都是通过instance 这个单子变量来访问FirebaseAuth

这并不推荐,因为所产生的代码是不可测试的。

2) 直接使用FirebaseAuth

在我们的小部件中直接使用FirebaseAuth 并不是一个好主意。

如果我们的应用程序增长了,并且我们决定在将来使用不同的认证提供者,这可能会引起问题。


下一篇文章中,我们将看到如何解决这些问题。我们将通过以下方式来实现。

  • 全局访问转移到使用提供者的范围访问
  • 编写一个认证服务类,作为 "认证提供者 "的封装器。FirebaseAuth

顺便说一下,我在我的Flutter & Firebase课程中涵盖了所有这些主题(以及更多)。

编码愉快