how do I verify a new user's email in Flutter and Firebase before granting them access to the app?

343

I think you just don't initiate Auth()

  late AuthBase auth;
  late Timer timer;
  User? user;
  bool isUserEmailVerified = false;

  @override
  void initState() {
    super.initState();
    auth = Auth();
    user = auth.currentUser;
    user?.sendEmailVerification();
    timer = Timer.periodic(
      const Duration(
        seconds: 5,
      ),
          (timer) {
        checkEmailVerified();
      },
    );
  }

Share:
343
Carleton Y
Author by

Carleton Y

Updated on December 16, 2022

Comments

  • Carleton Y
    Carleton Y over 1 year

    I am trying to have new users create an account, receive a verification email and then only be granted access to the app once they have clicked the verification link sent by Firebase. I am not getting any errors but there are clearly shortcomings in my code. The new user information is accepted and stored by Firestore and a verification email is sent but once the verification link is clicked the user remains on the Verify page instead of navigating to the HomePage (called Jobs Page in this code).

    I cannot figure this out after days of trying so any help would be greatly appreciated. I have included several pages of my code. Thanks in advance!

    abstract class AuthBase {
      User? get currentUser;
      Stream<User?> authStateChanges();
    
      Future<User?> signInWithEmailAndPassword(String email, String password);
      Future<void> createUserWithEmailAndPasswordVerify(
          String email, String password);
      Future<void> signOut();
    }
    
    class Auth implements AuthBase {
      // Value that retrieves an instance of the FirebaseAuth object. This is used for managing users between the app and the Firebase backend
      final _firebaseAuth = FirebaseAuth.instance;
    
      @override
      Stream<User?> authStateChanges() => _firebaseAuth.authStateChanges();
    
      @override
      User? get currentUser => _firebaseAuth.currentUser;
    
      @override
      Future<User?> signInWithEmailAndPassword(
          String email, String password) async {
        final userCredential = await _firebaseAuth.signInWithCredential(
          EmailAuthProvider.credential(
            email: email,
            password: password,
          ),
        );
        return userCredential.user;
      }
    
      @override
      Future<User?> createUserWithEmailAndPasswordVerify(
          String email, String password) async {
        final userCredential = await _firebaseAuth.createUserWithEmailAndPassword(
          email: email,
          password: password,
        );
        try {
          await userCredential.user?.sendEmailVerification();
        } catch (e) {
          print(
            'An error occurred while trying to send email verification',
          );
        }
        return userCredential.user;
      }
    
      @override
      Future<void> signOut() async {
        await GoogleSignIn().signOut();
        await _firebaseAuth.signOut();
      }
    }
    
    
    class VerifyPage extends StatefulWidget {
      const VerifyPage({
        Key? key,
      }) : super(key: key);
    
      @override
      State<VerifyPage> createState() => _VerifyPageState();
    }
    
    class _VerifyPageState extends State<VerifyPage> {
      AuthBase? auth;
      Timer? timer;
      User? user;
      bool isUserEmailVerified = false;
    
      @override
      void initState() {
        super.initState();
        user = auth?.currentUser;
        user?.sendEmailVerification();
        timer = Timer.periodic(
          const Duration(
            seconds: 5,
          ),
          (timer) {
            checkEmailVerified();
          },
        );
      }
    
      Future<void> checkEmailVerified() async {
        user = auth?.currentUser;
        await user?.reload();
        final signedInUser = user;
        if (signedInUser != null && signedInUser.emailVerified) {
          timer?.cancel();
          Navigator.pushReplacement(
            context,
            MaterialPageRoute(
              builder: (context) => const JobsPage(),
            ),
          );
        }
      }
    
      @override
      void dispose() {
        timer?.cancel();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            leading: Container(),
            title: const Text('Verify Email'),
          ),
          body: const Center(
            child: Padding(
              padding: EdgeInsets.all(16.0),
              child: Text(
                'An email has just been sent to your email.  Click on the link provided to complete registration.',
                style: TextStyle(
                  fontSize: 20.0,
                ),
              ),
            ),
          ),
        );
      }
    }
    

    code from email sign in form

    Future<void> _submitVerify() async {
        try {
          await model.submitVerify(context);
          Navigator.pushReplacement(
            context,
            MaterialPageRoute(
              builder: (context) => const VerifyPage(),
            ),
          );
        } on FirebaseAuthException catch (e) {
          showExceptionAlertDialog(
            context,
            exception: e,
            title: 'Sign In Failed',
          );
        }
      }
    
  • Carleton Y
    Carleton Y over 2 years
    your answer is exactly the medicine my problem needed. It worked perfectly. I can admit I would not have figured this out on my own. Thank you for being generous with your time and providing a solution.