【问题标题】:Flutter: How to listen to the FirebaseUser is Email verified boolean?Flutter:如何收听 FirebaseUser 是电子邮件验证的布尔值?
【发布时间】:2019-12-03 04:22:08
【问题描述】:

我的想法: 我想在 Flutter 中使用 Firebase Auth 插件来注册用户。 但在他们可以访问应用程序之前,他们必须验证他们的电子邮件地址。 因此,我在注册后将 Firebase 用户推送到验证屏幕。这只是一个加载屏幕,告诉用户他必须验证他的电子邮件。

但现在:如果用户的电子邮件已通过验证并将他(如果为真)发送到主屏幕,我该如何持续收听?

我是 Flutter 的新手,我不知道是否必须使用 Streams 或 Observables 或 while 循环或 setState() 或其他东西来进行这种布尔检查。而且我也不知道如何设置解决方案。

这是我注册用户的基本代码:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;
  final Firestore _db = Firestore.instance;

  Future<FirebaseUser> get getUser => _auth.currentUser();

  Stream<FirebaseUser> get user => _auth.onAuthStateChanged;

  Future<FirebaseUser> edubslogin(String email, String password) async {
    try {
      final FirebaseUser user = await _auth.createUserWithEmailAndPassword(
        email: email,
        password: password,
      );
     
      await user.sendEmailVerification();
      
      //email verification somewhere here
    
      updateUserData(user);
      return user;
    } catch (error) {
      print(error);
      return null;
    }
  }

我试过了:

     if (user.isEmailVerified == true) {
        
        //go to Homescreen
        return true; 
      } else {

        //show verification screen(loading spinner)
        return false;
      }

但我没有从 isEmailVerified 中得到布尔值 true

我该怎么办?

【问题讨论】:

    标签: firebase flutter dart firebase-authentication state-management


    【解决方案1】:

    此验证并不像您希望的那样简单。首先,存在识别用户已验证其电子邮件的问题。其次,存在的问题是,没有任何类型的通知可以让您自动触发应用的更改。

    查看此线程以获取有关 emailVerified 的信息:https://github.com/flutter/flutter/issues/20390#issuecomment-514411392

    只有在以下情况下,我才能验证用户:1) 创建了他们的帐户,2) 让他们登录,3) 然后检查以确保他们验证了他们的电子邮件。

    final FirebaseAuth _auth = FirebaseAuth.instance;
    
    var _authenticatedUser = await _auth.signInWithEmailAndPassword(email: _email, password: _password); 
    
    //where _email and _password were simply what the user typed in the textfields.
    
    
    
    if (_authenticatedUser.isEmailVerified) {
            //Verified
          } else {
            //Not verified
            }
    

    第 2 部分:如何让您的应用识别用户已确认他们的电子邮件?找到一种方法来触发检查确认的功能。一个按钮就很简单了。如果您希望它看到“自动”,那么我想您可以创建一个计时器,每 10 秒左右检查一次电子邮件验证。

    【讨论】:

    • 这里的关键是当用户验证他们的电子邮件地址时,应用程序不会自动收到通知,因此您需要在应用程序内自行检查。为此,您需要 force a reload 的用户配置文件,以便从服务器获取最新值。有了这些,您可以重新检查 isEmailVerified 的值。
    • "This verification isn't as straightforward as you'd hope" 尽可能简单直接。
    • 我完全同意@OjonugwaJudeOchalifu。可以通过 Alisson 的回答进行改进: Timer.periodic(fiveSec, (Timer t){ _auth.currentUser!..reload(); if(_auth.currentUser!.emailVerified){ //TODO: Proceed to specific page (route) }else { //TODO: 每五秒烤一次 } });
    【解决方案2】:

    我找到了一种方法,通过更新 firebase 用户配置文件并在 init() 中调用它,就像下面的函数一样。

    void _checkEmailVerification() async {
        await widget.auth.getCurrentUser().then((user) {
          UserUpdateInfo userUpdateInfo = new UserUpdateInfo();
          userUpdateInfo.displayName = user.displayName;
          user.updateProfile(userUpdateInfo).then((onValue) {
            setState(() {
              _isEmailVerified = user.isEmailVerified;
            });
          });
        });
      }
    

    【讨论】:

      【解决方案3】:

      我刚刚在我的应用程序中遇到了同样的情况。我的解决方案是在策略路由的 initState 方法中创建一个周期性计时器,以保持应用程序直到电子邮件得到验证。它不像使用监听器那么优雅,但效果很好。

      bool _isUserEmailVerified;
      Timer _timer;
      
      @override
      void initState() {
          super.initState();
          // ... any code here ...
          Future(() async {
              _timer = Timer.periodic(Duration(seconds: 5), (timer) async {
                  await FirebaseAuth.instance.currentUser()..reload();
                  var user = await FirebaseAuth.instance.currentUser();
                  if (user.isEmailVerified) {
                      setState((){
                          _isUserEmailVerified = user.isEmailVerified;
                      });
                      timer.cancel();
                  }
              });
          });
      }
      
      @override
      void dispose() {
          super.dispose();
          if (_timer != null) {
              _timer.cancel();
          }
      }
      

      【讨论】:

      • 这太棒了!谢谢!
      • import 'dart:async';
      【解决方案4】:

      我创建了一个流来处理这个问题。不是那么优雅但有效。使用 StreamProvider.value() 来处理事件。

        Stream<userVerificationStatus> checkUserVerified() async* {
          bool verified = false;
          yield userVerificationStatus(status: Status.LOADING); 
          while (!verified) {
            await Future.delayed(Duration(seconds: 5));
            FirebaseUser user = await _auth.currentUser();
            if(user!=null)await user.reload();
            if (user == null) {
              yield userVerificationStatus(status: Status.NULL);
            } else {
              print("isemailverified ${user.isEmailVerified}");
              await user.reload();
              verified = user.isEmailVerified;
              if(verified)
              yield userVerificationStatus(status: Status.VERIFIED);
              else
              yield userVerificationStatus(status: Status.NOT_VERIFIED);
            }
          }
        }
      

      【讨论】:

      • _auth 应该是 FirebaseAuth.Instance 而 userVerificationStatus 只是一个自定义枚举类
      【解决方案5】:

      由于authOnChanged 仅侦听登录和注销操作,因此在您的登录方法中,请先注销然后尝试登录。

      await _firebaseAuth.signOut();
      
      authResult = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
      
      return authResult.user;
      

      onAuthChanged中,当你控制user.isEmailVerified时,它会在你退出后生效,即使你还没有登录它也会更新用户,因为退出会触发你的onAuthChanged如果您尚未登录。

      这就像作弊,但我发现没有超时的唯一方法就是这样。

      【讨论】:

        【解决方案6】:

        为了让应用识别用户是否验证了他们的电子邮件,您可以通过简单的 user.reload 来实现。

        为了自己测试它,使用 onPressed 代码实现一个按钮:

         FlatButton(
            child: Text("check"),
            textColor: Colors.white,
            onPressed: () async {
            try {
              FirebaseUser user = await _firebaseAuth.currentUser();
              await user.reload();
              user = await _firebaseAuth.currentUser();
                print( user.isEmailVerified); 
                
        
             } catch (e) {
              return e.message;
            }
        }),
        

        【讨论】:

          【解决方案7】:

          身份验证状态更改侦听器对我不起作用。即使在用户验证了他的电子邮件之后,字段 isEmailVerified 仍然是 false

          我的解决方法: 从假设用户离开应用程序以验证他的电子邮件(这意味着应用程序已暂停)开始,他在验证后返回应用程序(应用程序恢复)。

          我所做的是将WidgetsBinding 附加到相关的有状态小部件,如果电子邮件经过验证,我想在该小部件中显示(但可以在其他地方完成)。这涉及两个步骤。

          第一步是附加绑定:

            @override
            void initState() {
              WidgetsBinding.instance.addObserver(this);
              super.initState();
            }
          
            @override
            void dispose() {
              WidgetsBinding.instance.removeObserver(this);
              super.dispose();
            }
          

          第二步是覆盖didChangeAppLifecycleState 以重新加载用户。我创建了一个函数来重新加载并设置一个新的 firebaseUser 对象

            void didChangeAppLifecycleState(AppLifecycleState state) {
              if (state == AppLifecycleState.resumed && !firebaseUser.isEmailVerified)
                refreshFirebaseUser().then((value) => setState(() {}));
              super.didChangeAppLifecycleState(state);
            }
            Future<void> refreshFirebaseUser() async {
              await firebaseUser.reload();
              firebaseUser = FirebaseAuth.instance.currentUser;
            }
          

          所以这基本上是在每次用户返回应用程序时重新加载 firebase 用户对象,而其电子邮件未经过验证。我选择了这个解决方案而不是设置和取消计时器,因为它避免了通过计时器设置重复动作,这对于这个特定问题来说可能是多余的。

          【讨论】:

            【解决方案8】:

            没错。如果用户验证他们的电子邮件,FirebaseAuth idTokenChanges()authStateChanges()userChanges() 都不会向您发送事件。我正在使用这些方法的组合在我的应用中获取电子邮件验证更新,它似乎运行良好。

            首先我检查 initState() 方法中的状态,如果电子邮件未通过验证,则启动计时器

              @override
              void initState() {
                super.initState();
                WidgetsBinding.instance.addObserver(this);
            
                //Get Authenticated user
                user = context.read<AuthenticationService>().currentUser();
                _isEmailVerified = user.emailVerified;
                if (!_isEmailVerified) _startEmailVerificationTimer();
            
                }
            

            我还会监听应用后台/前台事件,以防用户碰巧离开应用以确认他们的电子邮件(如果您也这样做,请将 WidgetsBindingObserver 添加到您的班级)

             @override
              void didChangeAppLifecycleState(AppLifecycleState state) {
                if (state == AppLifecycleState.resumed) {
                  user = context.read<AuthenticationService>().reloadCurrentUser();
                  if (user.emailVerified) {
                    setState(() {
                      _isEmailVerified = user.emailVerified;
                    });
                    timer?.cancel();
                  } else {
                    if (!timer.isActive) _startEmailVerificationTimer();
                  }
                } 
              }
            

            这是 _startEmailVerificationTimer() 方法

             _startEmailVerificationTimer() {
                timer = Timer.periodic(Duration(seconds: 5), (Timer _) {
                  user = context.read<AuthenticationService>().reloadCurrentUser();
                  if (user.emailVerified) {
                    setState(() {
                      _isEmailVerified = user.emailVerified;
                    });
                    timer.cancel();
                  }
                });
              }
            

            别忘了配置定时器

              @override
              void dispose() {
                timer?.cancel();
                WidgetsBinding.instance.removeObserver(this);
                super.dispose();
              }
            

            如果有人感兴趣,我的 Firebase 用户方法:

              User currentUser() {
                return _firebaseAuth.currentUser;
              }
            
              User reloadCurrentUser() {
                User oldUser = _firebaseAuth.currentUser;
                oldUser.reload();
                User newUser = _firebaseAuth.currentUser;
                return newUser;
              }
            

            【讨论】:

              【解决方案9】:

              我在使用最新版本的 firebase auth 时遇到了同样的问题。

              但我发现有一个功能可以重新加载当前登录的用户

                Future<bool> get userVerified async {
                  await FirebaseAuth.instance.currentUser.reload();
                  return FirebaseAuth.instance.currentUser.emailVerified;
                }
              

              【讨论】:

              • 是的,工作就像一个魅力。唯一的区别是我没有使用 Future,只是一个 bool 函数。
              【解决方案10】:

              检查当前用户电子邮件后引用令牌已验证为真

                      var user = FirebaseAuth.instance.currentUser;
                      await user?.reload();
                      if (user?.emailVerified == true) {
                        await FirebaseAuth.instance.currentUser?.getIdToken(true);
                        //rest code..
                      }
              

              如果这是正确的做事方式,请告诉我。

              【讨论】:

              • 我可以验证您的解决方案,尤其是 user.reload( ),是正确的方法。我正在使用 GetX 来跟踪更新小部件。像魅力一样工作。虽然我绝对应该让这个功能成为未来。干得好。
              猜你喜欢
              • 2020-05-08
              • 2020-09-20
              • 2021-06-24
              • 1970-01-01
              • 2018-06-28
              • 2020-07-16
              • 2021-05-13
              • 2021-07-21
              相关资源
              最近更新 更多