I’m developing a flutter app with firebase, and I have a user registration form with email validation page. I want to check if the entered email already exists in the firebase database during the validation process. However, I’ve encountered a challenge – I can’t use async within the validation function:
The argument type ‘Future<String?> Function(String?)’ can’t be assigned to the parameter type ‘String? Function(String?)?’
How can I achieve this email existence check without compromising form validation?
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import '../../components/validation.dart';
class SignUpView extends StatefulWidget {
const SignUpView({Key? key}) : super(key: key);
@override
State<SignUpView> createState() => _SignUpViewState();
}
class _SignUpViewState extends State<SignUpView> {
final formKey = GlobalKey<FormState>();
bool obscureText = true;
final fullNameController = TextEditingController();
final passwordController = TextEditingController();
final emailController = TextEditingController();
String? emailValidationError;
void togglePasswordVisibility() {
setState(() {
obscureText = !obscureText;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: buildSignUpForm(context),
),
),
),
);
}
Widget buildSignUpForm(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset(
'your_image_path',
width: 100,
height: 100,
),
SizedBox(height: MediaQuery.of(context).size.height * 0.05),
Text(
'Create your account',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
const SizedBox(height: 12),
const Text(
'Join us today! '
'Create your account by filling out the required information and start exploring our services. '
'Sign up now and be a part of our community.',
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildTextFormField(
label: 'Full Name',
controller: fullNameController,
validator: Validators.fullNameValidation,
),
const SizedBox(height: 12),
buildTextFormField(
label: 'Email',
controller: emailController,
keyboardType: TextInputType.emailAddress,
validator: (email) {
String? emailError = Validators.emailValidator(email);
if (emailError != null) {
setState(() {
emailValidationError = emailError;
});
return emailError;
} else {
emailValidationError = null; // Clear the error message
return null;
}
},
),
const SizedBox(height: 12),
buildPasswordField(),
],
),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () async {
if (formKey.currentState!.validate()) {
try {
await signUpWithEmailAndPassword();
} catch (error) {
if (kDebugMode) {
print('Error during registration: $error');
}
}
}
},
child: const Text('Sign Up'),
),
],
);
}
Widget buildTextFormField({
required String label,
required TextEditingController controller,
TextInputType? keyboardType,
String? hintText,
String? Function(String?)? validator,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
TextFormField(
controller: controller,
keyboardType: keyboardType,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
hintText: hintText ?? 'Enter your $label',
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: validator,
),
],
);
}
Widget buildPasswordField() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Password',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
TextFormField(
controller: passwordController,
obscureText: obscureText,
decoration: InputDecoration(
hintText: 'Enter your password',
suffixIcon: IconButton(
icon: Icon(obscureText ? Icons.visibility_off : Icons.visibility),
onPressed: togglePasswordVisibility,
),
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: Validators.passwordValidator,
),
],
);
}
Future<void> signUpWithEmailAndPassword() async {
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: emailController.text,
password: passwordController.text,
);
User? user = FirebaseAuth.instance.currentUser;
if (user != null) {
await user.sendEmailVerification();
await FirebaseFirestore.instance.collection('userdata').doc(user.uid).set(
{
'fullname': fullNameController.text,
},
);
}
}
Future<bool> isEmailAlreadyInUse(String email) async {
try {
final user = await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
if (user.isNotEmpty) {
return true;
}
return false;
} catch (error) {
if (kDebugMode) {
print('Error checking if email is in use: $error');
}
return false;
}
}
}
My goal is to perform this email existence check during form validation without compromising the validation process. What is the best way to achieve this?
2
Answers
I have omitted the 'Future' function from my codebase. The reason for this decision was that the function was initially instantiated after the Firebase signup validation process, which resulted in inaccurate data being returned. Consequently, I have revised my approach to email validation and now perform it within the context of the Firebase signup process.
You can’t directly use
async
operations within thevalidator
function because it expects a synchronous return. Instead, you can perform the check asynchronously and update the validation state upon completion. Here’s a streamlined approach:emailError
accordingly:checkEmailExists
from theonFieldSubmitted
oronEditingComplete
callback of the emailTextFormField
:validator
for the email field to use theemailError
state:This way, the email existence check occurs outside the
validator
, and the form only submits after the check is complete.