Reputation: 2862
I have a weird problem in flutter TextFormField. I implemented form validation in TextFormField. But onSaved() function doesn't get called after successful validation.
First I created basic Widget using TextFormField
--- In AppWidgets class ---
static Widget buildTextFormField(
String labelText,
String helperText,
IconData prefixIcon, {
Widget suffixIcon,
bool obscureText = false,
TextInputType keyboardType = TextInputType.text,
TextInputAction textInputAction = TextInputAction.none,
FocusNode focusNode,
ValueChanged<String> onFieldSubmitted,
TextEditingController controller,
FormFieldValidator<String> validator,
FormFieldSetter<String> onSaved,
bool isLightTheme = false,
}) {
return Theme(
data: isLightTheme
? AppThemesLight.textFormFieldThemeData
: AppThemesDark.textFormFieldThemeData,
child: TextFormField(
controller: controller,
validator: validator,
onSaved: onSaved,
keyboardType: keyboardType,
textInputAction: textInputAction,
focusNode: focusNode,
onFieldSubmitted: onFieldSubmitted,
obscureText: obscureText,
decoration: InputDecoration(
filled: true,
fillColor: isLightTheme
? AppColorsLight.textFieldFillColor
: AppColorsDark.textFieldFillColor,
labelText: labelText,
helperText: helperText,
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(AppDimensions.textFieldBorderRadius),
),
),
prefixIcon: Icon(
prefixIcon,
color: isLightTheme
? AppColorsLight.primaryTextColor
: AppColorsDark.primaryTextColor,
),
suffixIcon: suffixIcon,
),
),
);
}
From that, created email text from field.
static Widget buildEmailTextFormField(LoginState loginState) {
return AppWidgets.buildTextFormField(
'Email address',
'Your email address',
Icons.email,
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
focusNode: loginState.focusNodes[0],
onFieldSubmitted: (String value) {
print('submitted $value');
loginState.onFocusChanged(index: 0);
},
validator: (String email) {
print('validator $email');
return InputValidators.validateEmail(email);
},
onSaved: (String email) {
print('saved $email');
loginState.email = email;
},
);
}
Here is the email validator I used.
static String validateEmail(String email) {
Pattern pattern =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = new RegExp(pattern);
if (email.isEmpty)
return 'Email can\'t be empty';
else if (!regex.hasMatch(email))
return 'Enter valid email address';
else
return null;
}
I tested above code by putting print statement inside onSaved() function, but it's not printing after successful validation.
Upvotes: 28
Views: 38725
Reputation: 600
I was struggling with this issue and yet I don't found my answer but I solved yours
import 'package:flutter/material.dart';
/// Flutter code sample for [Form].
void main() => runApp(const FormExampleApp());
class FormExampleApp extends StatelessWidget {
const FormExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Form Sample')),
body: const FormExample(),
),
);
}
}
class FormExample extends StatefulWidget {
const FormExample({super.key});
@override
State<FormExample> createState() => _FormExampleState();
}
class _FormExampleState extends State<FormExample> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController textController = TextEditingController();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
hintText: 'Enter your email',
),
onSaved: (value) {
print('VALUE $value');
},
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
// Validate will return true if the form is valid, or false if
// the form is invalid.
if (_formKey.currentState!.validate()) {
// Process data.
_formKey.currentState!.save();
}
},
child: const Text('Submit'),
),
),
],
),
);
}
}
But my personal issue is that I've build the input like a component so I will need to create a component for form that use it and ok probably this is more easy to solve, anyway I like full code that's why I'm sharing this simple print .
Upvotes: 0
Reputation: 14938
Did you call this method formKey.currentState.save()
?
in my case i forgot to call this after adding this it has worked.
Upvotes: 4
Reputation: 2862
The onSaved()
function won't be called automatically after successful validation. We have to call _formKey.currentState.save()
manually to save our variables.
Form(
key: key,
child: TextFormField(
onSaved: (val) {
print('saved');
},
validator: (val) {
print('validating');
},
),
),
RaisedButton(
child: Text('Click me'),
onPressed: () {
if (key.currentState.validate()) {
key.currentState.save();
print('valid');
}
},
),
Upvotes: 64