In the previous parts of our blog series, we established the user interface, set up the SMTP mailer, and created Firebase Cloud Functions for handling email verification. Now, in Part 4, we will focus on implementing a snapshot listener in our Flutter application. This listener will monitor the user’s email verification status in real-time, allowing for a smooth and responsive user experience.
Purpose of the Snapshot Listener for Email Verification
The snapshot listener will continuously observe the Firestore database for changes in the user’s verification status. When a user clicks the verification link in their email, the associated Cloud Function updates their status in Firestore. Our listener will detect this change and automatically handle the user’s login process if their email verification is successful.
Implementing the Snapshot Listener for Email Verification
The following code snippet shows how to set up a snapshot listener to monitor the user’s email verification status:
loginSuccess() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
Stream<DocumentSnapshot<Map<String, dynamic>>> userStream = FirebaseFirestore.instance
.collection('users')
.where('email', isEqualTo: currentUserEmailId)
.limit(1)
.snapshots()
.map((querySnapshot) => querySnapshot.docs.first);
userStream.listen((documentSnapshot) async {
if (documentSnapshot.exists) {
print('User document found');
var userData = documentSnapshot.data();
var successStatus = userData!['emailRequestStatus'];
var ss = await prefs.getString('emailVerificationStatus');
if (successStatus == "verificationSuccess" && ss == 'verificationPending') {
loginBegin = true;
isLoading = true;
final emailFromStorage = currentUserEmailId;
setState(() {});
final UserCredential userCredential = await _auth.signInWithEmailAndPassword(
email: currentUserEmailId,
password: currentUserEmailId);
if (userCredential.user != null) {
// User is signed in
await prefs.setString('emailVerificationStatus', 'verificationSuccess');
print('Successfully signed in with email link!');
await FirebaseFirestore.instance
.collection('users')
.doc(documentSnapshot.id) // Use the document ID to update the specific document
.update({'emailRequestStatus': 'loginSuccess'});
await insertUserAndDeviceOnLogin(context);
}
}
}
});
}
Explanation of the Code
- Shared Preferences Initialization:
- The method starts by initializing SharedPreferences, which will store the user’s verification status locally.
- Creating a Stream to Listen for User Data:
- A stream is created using FirebaseFirestore.instance.collection(‘users’), filtering for documents where the email matches currentUserEmailId. The stream listens for real-time updates, ensuring that any changes in the database are reflected in the app immediately.
- Listening for Document Changes:
- The listen method is called on the stream to handle incoming document snapshots. When a document change occurs, it checks if the document exists.
- Retrieving User Data:
- If the document exists, the user data is retrieved, including the emailRequestStatus. It also checks the local stored status to determine if verification is still pending.
- Processing Successful Verification:
- If the verification status is “verificationSuccess” and the local status is ‘verificationPending’, the application sets loginBegin and isLoading to true to indicate that a login process is starting.
- Signing in the User:
- The app attempts to sign in the user using their email and password. If successful, it updates the stored verification status in SharedPreferences to ‘verificationSuccess’.
- Updating Firestore:
- The function updates the Firestore document for the user, changing the emailRequestStatus to ‘loginSuccess’. This keeps the database in sync with the user’s current login state.
- Inserting User and Device Details:
- After successfully logging in, the function calls insertUserAndDeviceOnLogin(context) to handle any additional setup required for the user’s session.
Benefits of Using Snapshot Listeners
- Real-Time Updates: Snapshot listeners provide real-time data synchronization. Users receive immediate feedback when their email verification status changes, enhancing the overall experience.
- Reduced Latency: The listener automatically fetches updated data without requiring manual refreshes, minimizing wait times and keeping users engaged.
- Simplified Logic: Managing the state based on data changes is more straightforward with snapshot listeners, reducing the complexity of the app’s logic.
Conclusion
In this part of our blog series, we implemented a snapshot listener to monitor the email verification status of users in real-time. This setup enhances the user experience by providing immediate feedback and seamless transitions during the login process.
In the final part of our series, we will cover end-to-end testing of the email verification flow, ensuring that all components work harmoniously together. Stay tuned for Part 5, where we will conclude our implementation journey!