用户认证是很多应用程序的一个非常普遍的要求。
在这篇文章中,我们用不到100行的代码在Flutter中实现了一个简单的认证流程。
作为其中的一部分,我们将看到如何。
- 使用FirebaseAuth进行匿名登录。
- 使用
StreamBuilder来根据用户的认证状态呈现不同的屏幕。
这是我在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.plist和google-services.json,否则应用程序会在启动时崩溃。 - 在Firebase控制台中启用匿名登录,因为我们将在这个例子中使用它。
我在我的Flutter & Firebase课程中详细介绍了所有这些步骤。
让我们编写代码
我们的应用程序将有两个页面,叫做SignInPage 和HomePage ,它们都是无状态的部件。
然后我们将有另一个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流。每次用户签入或签出时,它都会收到一个新的值。- 一个类型为
FirebaseUser的StreamBuilder。它把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() 传递给MaterialApp 的home 参数。
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课程中涵盖了所有这些主题(以及更多)。
编码愉快