When trying to delete a user in my app, I’m running into an issue where the logic throws an error, specifically when trying to delete the user document from Firebase.
To delete the user, I check the provider, reauthenticate them with their given provider, and then remove any necessary fields in Firebase, and finally delete the necessary Firestore user document and update shared preferences. The code for the deleteUser method is below:
Future<void> deleteUser(BuildContext context) async {
await NotificationUtils.fcmUnSubscribe();
User? user = FirebaseAuth.instance.currentUser;
SharedPreferences prefs = await SharedPreferences.getInstance();
String userType = prefs.getString('userType') ?? '';
DocumentSnapshot<Map<String, dynamic>> userDoc = await FirebaseFirestore
.instance
.collection(userType)
.doc(user?.uid)
.get();
if (user == null) {
return; // Handle edge cases where user or userDoc is null
}
String provider = user.providerData[0].providerId;
// Check if the user is signed in with email/password
if (provider == 'password') {
// Prompt the user to re-enter their password
if (!context.mounted) return;
AuthCredential? credential = await passwordReauthenticationDialog(context);
try {
if (credential == null) return;
await user.reauthenticateWithCredential(credential);
} on FirebaseAuthException catch (e) {
if (!context.mounted) return;
showErrorMessage(e.code, context);
return;
}
} else if (provider == 'phone') {
// Prompt the user to enter verification code
if (!context.mounted) return;
AuthCredential? credential = await phoneReauthenticationDialog(context);
try {
if (credential == null) return;
await user.reauthenticateWithCredential(credential);
} on FirebaseAuthException catch (e) {
if (!context.mounted) return;
showErrorMessage(e.code, context);
return;
}
} else if (provider == 'google.com') {
GoogleSignInAccount? gUser = await GoogleSignIn().signIn();
if (gUser == null) {
return; // User canceled sign-in, exit without error
}
// Obtain auth details from request
GoogleSignInAuthentication? gAuth = await gUser.authentication;
// Create a new credential for user
AuthCredential credential = GoogleAuthProvider.credential(
accessToken: gAuth.accessToken,
idToken: gAuth.idToken,
);
// Reauthenticate user with the stored credential
try {
await user.reauthenticateWithCredential(credential);
} on FirebaseAuthException catch (e) {
if (!context.mounted) return;
showErrorMessage(e.code, context);
return;
}
} else if (provider == 'facebook.com') {
AccessToken? token = await FacebookAuth.instance.accessToken;
if (token == null) {
return; // User canceled sign-in, exit without error
}
// Create a new credential for user
AuthCredential credential = FacebookAuthProvider.credential(token.token);
// Reauthenticate user with the stored credential
try {
await user.reauthenticateWithCredential(credential);
} on FirebaseAuthException catch (e) {
if (!context.mounted) return;
showErrorMessage(e.code, context);
return;
}
}
// Proceed with user deletion
if (!context.mounted) return;
bool confirmDelete = await showConfirmationDialog(context);
if (confirmDelete) {
// Your existing deletion logic
if (userType == 'seniors') {
if (userDoc.exists) {
// Delete user document
await FirebaseFirestore.instance
.collection('seniors')
.doc(user.uid)
.delete();
await FirebaseFirestore.instance
.collection('tickets')
.doc(user.uid)
.delete();
}
} else if (userType == 'teens') {
if (!context.mounted) return;
if (userDoc.exists) {
// Check if the acceptedList field exists in the user's document
if (userDoc.get('acceptedList') != null) {
List<String> acceptedList =
List<String>.from(userDoc.get('acceptedList'));
// Update isAccepted field in tickets collection and set fields to null
WriteBatch batch = FirebaseFirestore.instance.batch();
for (String ticketId in acceptedList) {
DocumentReference ticketRef =
FirebaseFirestore.instance.collection('tickets').doc(ticketId);
batch.update(ticketRef, {
'isAccepted': false,
'teenFirstName': null,
'teenLastName': null,
'teenID': null,
});
}
batch.commit();
}
// Delete user document
await FirebaseFirestore.instance
.collection('teens')
.doc(user.uid)
.delete();
}
}
// Perform sign-out based on the provider
if (provider == 'google.com') {
// Google sign out
await GoogleSignIn().currentUser?.clearAuthCache();
await GoogleSignIn().signOut();
} else if (provider == 'facebook.com') {
// Facebook sign out
await FacebookAuth.instance.logOut();
}
// Delete the user
await user.delete();
// Clear SharedPreferences
prefs.setBool('isLoggedIn', false);
prefs.setString('userType', '');
// Navigate to the desired page
if (!context.mounted) return;
context.go('/selectionPage');
}
}
The issue is specifically on the line:
await FirebaseFirestore.instance
.collection('teens')
.doc(user.uid)
.delete();
Because when adding in debug print statements, it gets to this point and then the app crashes and throws a lot of errors, including a microTaskLoop error. Usually I only get this error trying to access something that doesn’t exist in Firestore, but I’m not sure why that would be the case here. I tried to move this delete call out of the if statement, but that also didn’t help.