Flutter:如何收听FirebaseUser是电子邮件验证的布尔值?

时间:2019-07-25 00:01:33

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

我的想法: 我想在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;
      }

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

我该怎么办? 非常感谢您的帮助:)

9 个答案:

答案 0 :(得分:14)

我在应用程序中遇到了同样的情况。我的解决方案是在战略路线的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();
    }
}

答案 1 :(得分:2)

为了使应用识别用户是否已验证电子邮件,您可以通过简单的 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;
    }
}),

答案 2 :(得分: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秒左右检查一次电子邮件验证。

答案 3 :(得分:1)

好吧,我创建了一个流来处理这个问题。不太优雅,但可以。使用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);
      }
    }
  }

答案 4 :(得分:0)

我找到了一种更新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;
        });
      });
    });
  }

答案 5 :(得分:0)

由于authOnChanged仅监听登录和退出操作,因此在您的登录方法中,请先退出然后再尝试登录。

await _firebaseAuth.signOut();

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

return authResult.user;

onAuthChanged中,当您控制user.isEmailVerified是否有效时,由于您已经注销,因此它将起作用,即使您尚未登录,它也会更新用户,因为注销会触发您的onAuthChanged,即使您尚未登录。

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

答案 6 :(得分:0)

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

我的解决方法: 从假设用户离开应用程序来验证其电子邮件(这意味着应用程序已已暂停)开始,然后他在验证应用程序(应用程序 resumes )后返回了该应用程序。 / p>

我所做的是将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用户对象,而其电子邮件未得到验证。我选择此解决方案而不是设置和取消计时器,因为它避免了通过计时器设置重复动作,对于这个特定问题而言,这可能是过大的选择。

答案 7 :(得分:0)

是的。如果用户验证了电子邮件,则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;
  }

答案 8 :(得分:0)

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

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

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