我在 Flutter 中使用 Firebase Auth 和 google 登录。我可以登录,但是当我关闭应用程序(杀死它)时,我必须重新注册。那么有没有一种方法可以保留用户身份验证,直到用户专门注销为止? 这是我的授权类
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
class Auth {
FirebaseAuth _firebaseAuth;
FirebaseUser _user;
Auth() {
this._firebaseAuth = FirebaseAuth.instance;
}
Future<bool> isLoggedIn() async {
this._user = await _firebaseAuth.currentUser();
if (this._user == null) {
return false;
}
return true;
}
Future<bool> authenticateWithGoogle() async {
final googleSignIn = GoogleSignIn();
final GoogleSignInAccount googleUser = await googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
this._user = await _firebaseAuth.signInWithGoogle(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
if (this._user == null) {
return false;
}
return true;
// do something with signed-in user
}
}
这是我的起始页面,其中调用了身份验证检查。
import 'package:flutter/material.dart';
import 'auth.dart';
import 'login_screen.dart';
import 'chat_screen.dart';
class Splash extends StatefulWidget {
@override
_Splash createState() => _Splash();
}
class _Splash extends State<Splash> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(
value: null,
),
),
);
}
@override
void initState() {
super.initState();
_handleStartScreen();
}
Future<void> _handleStartScreen() async {
Auth _auth = Auth();
if (await _auth.isLoggedIn()) {
Navigator.of(context).pushReplacementNamed("/chat");
}
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) => LoginScreen(auth: _auth,)));
}
}
我相信你的问题是路由。在我的应用程序中,我使用
FirebaseAuth
,它的工作原理就像你说的那样,而且我不保留任何登录令牌。但是,我不知道为什么你使用 getUser 的方法不起作用。
尝试调整您的代码以使用
onAuthStateChanged
。编辑:截至 2022 年,对于 Flutter 3,我注意到使用 userChanges
效果更好。
基本上,在您的
MaterialApp
上,创建一个 StreamBuilder
监听 _auth.userChanges()
并根据身份验证状态选择您的页面。
我将复制并粘贴我的应用程序的部分内容,以便您有一个想法(见下文)。
[...]
final FirebaseAuth _auth = FirebaseAuth.instance;
Future<void> main() async {
FirebaseApp.configure(
name: '...',
options:
Platform.isIOS
? const FirebaseOptions(...)
: const FirebaseOptions(...),
);
[...]
runApp(new MaterialApp(
title: '...',
home: await getLandingPage(),
theme: ThemeData(...),
));
}
Future<Widget> getLandingPage() async {
return StreamBuilder<FirebaseUser>(
stream: _auth.userChanges(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData && (!snapshot.data!.isAnonymous)) {
return HomePage();
}
return AccountLoginPage();
},
);
}
更新:截至 2024 年,我相信推荐的方法是使用 go_router。从这个意义上说,该代码将更改为类似以下内容:
[...]
final GoRouter _router = GoRouter(
refreshListenable: GoRouterRefreshStream(FirebaseAuth.instance.userChanges()),
runApp(MaterialApp.router(
[...]
routeInformationProvider: _router.routeInformationProvider,
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
));
[...]
但是(抱歉!),似乎 GoRouterRefreshStream 已从 go_router 中删除,然后我还不知道新的做法是什么。
抱歉,这是我的错误。忘记将推送登录屏幕放在 else 中。
Future<void> _handleStartScreen() async {
Auth _auth = Auth();
if (await _auth.isLoggedIn()) {
Navigator.of(context).pushReplacementNamed("/chat");
}
else {
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) => LoginScreen(auth: _auth,)));
}
}
void main() {
FirebaseAuth.instance.authStateChanges().listen((User user) {
if (user == null) {
runApp(MyApp(auth : false);
} else {
runApp(MyApp(auth : false);
}
});
}
class MyApp extends StatefulWidget {
final bool auth;
MyApp({this.auth});
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
return MaterialApp(
......
......
home: widget.auth ? MainScreen() : AuthScreen();
);
即使您终止了应用程序,您也可以使用
shared_preferences
来保持会话处于活动状态。
这是文档https://pub.dartlang.org/packages/shared_preferences。
我还听说可以使用 sqlite 来持久化会话。
添加此代码。应该可以正常工作。
FirebaseAuth auth = FirebaseAuth.instance;
auth.setPersistence(Persistence.SESSION);
你可以使用我的代码,你可以使用 userChanges() 而不是 authStateChanges()
通知任何用户更新的更改。
这是 [authStateChanges] 和 [idTokenChanges] 的超集。它提供所有用户更改的事件,例如何时链接、取消链接凭证以及何时更新用户配置文件。此流的目的是监听用户状态的实时更新(登录、注销、不同用户和令牌刷新),而无需手动调用 [reload],然后重新对应用程序进行更改。
final Stream<User?> firebaseUserChanges = firebaseAuth.userChanges();
再举一个简单的例子:
Future<bool> isUserLoggedIn() async {
final User? user = FirebaseAuth.instance.currentUser;
return user != null;
}
class InitialScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<bool>(
future: isUserLoggedIn(),
builder: (_, snapshot) {
if (snapshot.hasData) {
if (snapshot.data ?? false) {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => UnauthScreen()));
} else {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => HomeScreen()));
}
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
}
我可以通过检查 firebase 实例 currentUser 值来实现它。如果为 null,我将路由到我的注册页面。如果没有,那么我就会路由到我的主页。不确定这个实现是否有任何问题(到目前为止运行良好),但似乎比上面发布的 StreamBuilder 解决方案更简单。
home: getLandingPage(),
routes: {
(...)
}
Widget getLandingPage() {
if (_auth.currentUser == null) {
return SignupPage();
} else {
return HomePage();
}
}