I'm new to Flutter and new as a developer. I have reviewed dozens of Stackoverflow answers on this topic, read the FlutterFire documentation on verifying an email and reviewed several Medium articles in search of an answer. The documentation is very well written and the articles are also well done but for an inexperienced developer some things just don't sink in easily.
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',
);
}
}
CodePudding user response:
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();
},
);
}