简体   繁体   English

持久化用户Auth Flutter Firebase

[英]Persist user Auth Flutter Firebase

I am using Firebase Auth with google sign in Flutter. I am able to sign in however when I close the app(kill it), I have to sign up all over again.我正在使用 Firebase Auth with google sign in Flutter。我可以登录但是当我关闭应用程序(杀死它)时,我必须重新注册。 So is there a way to persist user authentication till specifically logged out by the user?那么有没有办法在用户专门注销之前一直保留用户身份验证? Here is my auth class这是我的身份验证 class

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
  }
}

Here is my start page where the auth check is called.这是我的开始页面,其中调用了身份验证检查。

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

I believe your problem is routing.我相信你的问题是路由。 In my apps I use FirebaseAuth and it works just as you say you wanted to, and I don't persist any login token.在我的应用程序中,我使用FirebaseAuth ,它就像你说的那样工作,而且我不保留任何登录令牌。 However, I don't know why your approach of using a getUser is not working.但是,我不知道为什么您使用 getUser 的方法不起作用。

Try to adjust your code to use onAuthStateChanged .尝试调整您的代码以使用onAuthStateChanged EDIT: As of 2022, with Flutter 3, I noticed it worked better with userChanges instead.编辑:截至 2022 年,使用 Flutter 3,我注意到它与userChanges效果更好。

Basically, on your MaterialApp , create a StreamBuilder listening to _auth.userChanges() and choose your page depending on the Auth status.基本上,在您的MaterialApp上,创建一个StreamBuilder监听_auth.userChanges()并根据身份验证状态选择您的页面。

I'll copy and paste parts of my app so you can have an idea:我将复制并粘贴我的应用程序的部分内容,以便您有一个想法:

[...]

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

Sorry, it was my mistake.对不起,是我的错。 Forgot to put the push login screen in 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();
);

You can use shared_preferences to keep alive your session even when you kill the app.即使您终止应用程序,您也可以使用shared_preferences来保持会话活动。 Here is the documentation https://pub.dartlang.org/packages/shared_preferences .这是文档https://pub.dartlang.org/packages/shared_preferences

Also I've heard that it's possible to use sqlite to persist the session.另外我听说可以使用 sqlite 来保持会话。

Add this code.添加此代码。 It should work fine.它应该可以正常工作。

FirebaseAuth auth = FirebaseAuth.instance;

auth.setPersistence(Persistence.SESSION);

You can use my code, You can use userChanges() instead of authStateChanges()您可以使用我的代码,您可以使用 userChanges() 而不是 authStateChanges()

Notifies about changes to any user updates.通知任何用户更新的更改。

This is a superset of both [authStateChanges] and [idTokenChanges].这是 [authStateChanges] 和 [idTokenChanges] 的超集。 It provides events on all user changes, such as when credentials are linked, unlinked and when updates to the user profile are made.它提供有关所有用户更改的事件,例如何时链接、取消链接凭据以及何时更新用户配置文件。 The purpose of this Stream is for listening to realtime updates to the user state (signed-in, signed-out, different user & token refresh) without manually having to call [reload] and then rehydrating changes to your application.此 Stream 的目的是侦听用户状态的实时更新(登录、注销、不同用户和令牌刷新),而无需手动调用 [reload] 然后重新对应用程序进行更改。

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

One more simple example:还有一个简单的例子:

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

I was able to achieve it by checking the firebase instance currentUser value.我能够通过检查 firebase 实例 currentUser 值来实现它。 if null I routed to my Signup page.如果 null 我路由到我的注册页面。 If not, then I routed to my HomePage.如果没有,那么我会转到我的主页。 Not sure if there is anything wrong with this implementation (its working well so far) but seems simpler than the StreamBuilder solution posted above.不确定此实现是否有任何问题(目前运行良好)但似乎比上面发布的 StreamBuilder 解决方案更简单。

  home: getLandingPage(),
        routes: {
          (...)
 }

    Widget getLandingPage() {
  if (_auth.currentUser == null) {
    return SignupPage();
  } else {
    return HomePage();
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM