[英]How to manage Firebase Authentication state in Flutter?
我有一个WelcomeScreen
,其中包含注册和登录,以及我想在用户登录后重定向的HomeScreen
。为了管理身份验证数据,我创建了一个auth.dart
和static
属性和方法,以便我可以在所有页面上访问它们相同的数据。
import 'package:firebase_auth/firebase_auth.dart';
class Auth {
static final auth = FirebaseAuth.instance;
static Future<void> logout() async {
await auth.signOut();
}
static Future<void> loginUser(String userEmail, String userPassword) async {
await auth.signInWithEmailAndPassword(email: userEmail, password: userPassword);
}
static Future<FirebaseUser> getCurrentUser() async {
return await auth.currentUser();
}
}
在main.dart
文件中,我使用StreamBuilder
根据更改的身份验证数据更改当前屏幕。 我从这个答案中得到了这个StreamBuilder
代码。
home: StreamBuilder<FirebaseUser>(
stream: Auth.auth.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.hasData) {
return HomeScreen();
} else {
return WelcomeScreen();
}
},
),
在我的登录屏幕中,我使用以下代码触发登录:
Future<void> login() async {
...
try {
await Auth.loginUser(userEmail, userPassword);
var user = await Auth.getCurrentUser();
print(user.displayName); // This works
} catch (error) {
print(error.message);
}
}
我不知道我使用的 static 方法是否是处理 Firebase 身份验证的正确方法,但它似乎有效。 登录后,我可以显示登录用户的名称,但StreamBuilder
中的main.dart
没有反映更新的身份验证数据,即没有更改页面。
是因为 static 方法还是StreamBuilder
的实现有问题?
provider
)完整代码:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
ChangeNotifierProvider<AuthModel>(
create: (_) => AuthModel(),
child: MaterialApp(
home: Consumer<AuthModel>(
builder: (_, auth, __) => auth.isSignedIn ? HomePage() : WelcomePage(),
),
),
),
);
}
class WelcomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('WelcomePage')),
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => LoginPage())),
child: Text('Go to Login Page'),
),
),
);
}
}
class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login Page')),
body: Center(
child: ElevatedButton(
onPressed: () async {
final model = context.read<AuthModel>();
await model.signIn(email: 'test@test.com', password: 'test1234');
Navigator.pop(context);
},
child: Text('Login'),
),
),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('HomePage')),
body: Center(
child: FloatingActionButton.extended(
onPressed: () async {
final model = context.read<AuthModel>();
await model.signOut();
},
label: Text('Log out'),
),
),
);
}
}
class AuthModel extends ChangeNotifier {
final FirebaseAuth _auth = FirebaseAuth.instance;
bool get isSignedIn => _auth.currentUser != null;
Future<void> signIn({required String email, required String password}) async {
await _auth.signInWithEmailAndPassword(email: email, password: password);
notifyListeners();
}
Future<void> signOut() async {
await _auth.signOut();
notifyListeners();
}
}
在我看来,在 flutter 中管理 firebase 身份验证的最佳方法是使用提供程序 package。 您的 Auth class 缺少一件重要的事情,即 onAuthStateChnaged 方法。 您可以创建 stream 作为 Auth class 内的 onAuthStateChanged 的 getter。 Auth class 将扩展 ChangeNotifier class。 ChangeNotifier class 是 flutter api 的一部分。
class Auth extends ChangeNotifier {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create a getter stream
Stream<FirebaseUser> get onAuthStateChanged => _auth.onAuthStateChanged;
//Sign in async functions here ..
}
用 ChangeNotifierProvider(提供程序包的一部分)包装您的 MaterialApp,并在 create 方法中返回 Auth class 的实例,如下所示:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Auth(),
child: new MaterialApp(
home: Landing(),
),
);
}
}
现在将登录页面创建为无状态小部件。 使用 Consumer 或 Provider.of(context) 和 stream 构建器来监听 auth 更改并根据需要呈现登录页面或主页。
class Landing extends StatelessWidget {
@override
Widget build(BuildContext context) {
Auth auth = Provider.of<Auth>(context);
return StreamBuilder<FirebaseUser>(
stream: auth.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
FirebaseUser user = snapshot.data;
if (user == null) {
return LogIn();
}
return Home();
} else {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
}
您可以从官方 flutter 文档中阅读有关 state 管理与提供程序的更多信息。 按照这个链接: https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
截屏:
我不确定你是怎么做的,所以我添加了一个最小的工作代码,我没有对你的Auth
class 进行任何更改。 虽然使用Provider
是个好主意,但您也可以使用static
方法完成任务。
编辑代码:
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<FirebaseUser>(
stream: Auth.auth.onAuthStateChanged,
builder: (context, snapshot) {
if (snapshot.hasData) return HomeScreen();
else return WelcomeScreen();
},
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home Screen')),
floatingActionButton: FloatingActionButton.extended(
label: Text('Sign out'),
onPressed: Auth.logout,
),
);
}
}
class WelcomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Welcome Screen')),
body: Center(
child: RaisedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => LoginPage())),
child: Text('Go to Login Page'),
),
),
);
}
}
class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login Page')),
body: Center(
child: RaisedButton(
onPressed: () async {
await Auth.loginUser('test@test.com', 'test1234');
await Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (_) => MyApp()), (_) => false);
},
child: Text('Login'),
),
),
);
}
}
我制作了一个视频 ( https://youtu.be/iqy7xareuAI ) 讨论这个赏金并带您完成实现所需应用程序的步骤。 它所需要的只是一个简单的StreamBuilder
和一个FutureBuilder
。
更复杂的工具,如provider
和singleton pattern
(您试图通过 static 类实现的目标)可以应用于更复杂的应用程序,但这里不需要。
这是 WelcomeScreen 的代码:
import 'package:ctfultterfireexperiments/src/screens/home_screen.dart';
import 'package:ctfultterfireexperiments/src/screens/login_signup_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class WelcomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext _, AsyncSnapshot<FirebaseUser> snapshot) {
//if the snapshot is null, or not has data it is signed out
if(! snapshot.hasData) return LoginSignupScreen();
// if the snapshot is having data it is signed in, show the homescreen
return HomeScreen();
},
);
}
}
这是 HomeScreen.dart 的代码
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(
child: Center(
child: FutureBuilder(
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if(!snapshot.hasData) return LinearProgressIndicator();
return Text("Home Screen: ${snapshot.data.displayName}");
},
future: FirebaseAuth.instance.currentUser(),
)),
),
Spacer(),
RaisedButton(onPressed: () {FirebaseAuth.instance.signOut();})
],
);
}
}
这是 LoginSignupScreen.dart 的代码:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
class LoginSignupScreen extends StatelessWidget {
login() async{
final GoogleSignIn _googleSignIn = GoogleSignIn();
final _auth = FirebaseAuth.instance;
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
final FirebaseUser user = (await _auth.signInWithCredential(credential)).user;
print("signed in " + user.displayName);
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Spacer(flex: 1,),
Text("Login/Signup Screen"),
Spacer(flex: 2,),
RaisedButton(onPressed: login)
],
);
}
}
这将作为一个最小的工作示例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.