持久化用户身份验证 Flutter Firebase

Posted

技术标签:

【中文标题】持久化用户身份验证 Flutter Firebase【英文标题】:Persist user Auth Flutter Firebase 【发布时间】:2019-06-25 10:08:02 【问题描述】:

我在 Flutter 中使用 Firebase Auth 和谷歌登录。我可以登录,但是当我关闭应用程序(杀死它)时,我必须重新注册。那么有没有办法保持用户身份验证直到用户特别注销? 这是我的身份验证课程

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,)));
  

【问题讨论】:

????您可以添加用于登录的代码吗?通常你不需要坚持任何事情来实现这一点 - 它只是工作。添加您检测到用户已经登录的代码。它可能是您的路由逻辑中的一个问题。 我在应用启动时运行 isLoggedIn() 来决定是从登录屏幕还是主屏幕启动。 @Feu 发布了代码。 【参考方案1】:

我相信你的问题是路由。在我的应用程序中,我使用FirebaseAuth,它就像你说的那样工作,而且我不保留任何登录令牌。但是,我不知道为什么您使用 getUser 的方法不起作用。

尝试调整您的代码以使用onAuthStateChanged

基本上,在您的MaterialApp 上,创建一个StreamBuilder 监听_auth.onAuthStateChanged 并根据身份验证状态选择您的页面。

我将复制并粘贴我的应用程序的部分内容,以便您有所了解:

[...]

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.onAuthStateChanged,
    builder: (BuildContext context, snapshot) 
      if (snapshot.hasData && (!snapshot.data.isAnonymous)) 
        return HomePage();
      

      return AccountLoginPage();
    ,
  );

【讨论】:

我添加了我正在检查已登录用户以路由到正确页面的主页。 不要像现在这样进行检查,使用 OnAuthStateChanged! 好的。我会去做。另外,你能告诉我这种方法出了什么问题吗?我想知道。 也许在您请求的那一刻,有关已登录用户的信息不可用 - 但这只是一个理论。在收听 onAuthStateChange 时,也许该信息稍后会变得可用,然后您就可以捕捉到它。或者也许这只是一个错误! ? 作为此更改的一个很好的副作用,当您调用 _auth 注销方法时,此流也会触发该更改,将页面更改为自动登录! 太棒了!因为它正在工作,你能接受它作为答案吗?【参考方案2】:

对不起,这是我的错误。忘记把推送登录界面放在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,)));
    
  

【讨论】:

是的,至少现在我知道这不是 firebase 错误。【参考方案3】:
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();
);

【讨论】:

【参考方案4】:

您可以使用shared_preferences 使您的会话保持活跃,即使您终止了应用程序。 这是文档https://pub.dartlang.org/packages/shared_preferences。

我还听说可以使用 sqlite 来保持会话。

【讨论】:

但是在 sqllite 中存储什么。另外我正在尝试制作一个聊天应用程序,因此它需要保持授权才能获得新的通知。 通常你存储用户的信息,但我认为令牌是最重要的存储。 好的,我试试看。 我希望你能成功!祝你好运!。【参考方案5】:

添加此代码。它应该可以正常工作。

FirebaseAuth auth = FirebaseAuth.instance;

auth.setPersistence(Persistence.SESSION);

【讨论】:

未处理异常:UnimplementedError:setPersistence() 仅在基于 Web 的平台上受支持 @nadeesh 试试这个:if(kIsWeb) auth.setPersistence(Persistence.SESSION); 请查看文档,它仅适用于基于 Web 的平台。【参考方案6】:

你可以使用我的代码,你可以使用 userChanges() 代替 authStateChanges()

通知任何用户更新的更改。

这是 [authStateChanges] 和 [idTokenChanges] 的超集。它提供有关所有用户更改的事件,例如何时链接、取消链接凭据以及何时更新用户配置文件。此 Stream 的目的是侦听用户状态的实时更新(登录、退出、不同用户和令牌刷新),而无需手动调用 [reload] 然后重新对您的应用程序所做的更改。

final Stream<User?> firebaseUserChanges = firebaseAuth.userChanges();

【讨论】:

以上是关于持久化用户身份验证 Flutter Firebase的主要内容,如果未能解决你的问题,请参考以下文章

Flutter Firebase auth 用户在设备上不是持久的

如何在 Flutter 应用程序中更新 Firebase 令牌并存储它?

如何在启用电话身份验证的情况下导出 Firebase 用户帐户

Flutter:Firebase身份验证无需登录即可创建用户

Flutter Firebase 不保留用户身份验证

Firebase 身份验证 Flutter 中的电话号码