Optional TextFormField in Form - flutter

I have 1 Form with 2 TextFormField, firstName and lastName.
I need to validate this two data with condition and regex, then pass data to the second page with Navigator.
Problem is, i want lastName is optional or can be empty.
But i can't remove validator because i still need to use it with regex or others.
TextEditingController firstName = TextEditingController();
TextEditingController lastName = TextEditingController();
GlobalKey<FormState> _key = GlobalKey();
bool _validate = false;
String first, last;
Form(
key: _key,
autovalidate: _validate,
child: Column(children: [
InputName(
controller: firstName,
placeholder: 'Nama depan',
validator: validateFirstName,
onSaved: (String val) {
first = val;
},
),
InputName(
controller: lastName,
placeholder: 'Nama belakang',
validator: validateLastName,
onSaved: (String val) {
last = val;
},
),
]),
),
Button(
text: 'Lanjut mengisi Email',
onPressed: () {
if (_key.currentState.validate()) {
_key.currentState.save();
String a = firstName.text.capitalize();
String b = lastName.text.capitalize();
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation1, animation2) =>
Regist2Page([a, b]),
transitionDuration: Duration(seconds: 0),
),
);
} else {
setState(() {
_validate = true;
});
}
},
),
String validateLastName(String value) {
if (value.isNotEmpty) {
if (value.length > 20) {
return 'Maksimal 20 karakter';
}
return null;
}
return null;
}

Try this validator
String validateLastName(String value) {
if (value == "") {
// if value were null, is true and then return null
return null;
}
if (value.length > 20) {
return 'Maksimal 20 karakter';
}
return null;
}

Try this!
String validateLastName(String value,{bool isOptional = false})) {
if(isOptional && (value==null || value.isEmpty)){
return null;
}
if (value.isNotEmpty) {
if (value.length > 20) {
return 'Maksimal 20 karakter';
}
return null;
}
return null;
}

Solved by Agus Setya R.
String b = lastName.text == '' ? '' : lastName.text.capitalize();

Related

How to limit text field with a value in flutter?

I want to limit the value that user input not to over a expected value in flutter text field.
Example,
If there is a value come from API is 10 and want to limit the input not over 10, if client type 11 or something over 10, want to show alert or make user not to type.
How to control this?
TextFormField(
style: TextStyle(fontSize: 20),
onChanged: (value) {
if (value != "") {
int _checkValue = int.parse(value);
if (_checkValue >
Provider.of<SaleProvider>(context, listen: false)
.remainNewQuantity(
this.currentProductItemSelected.id)) {
return 'error';
} else {
setState(() {
this.qty = int.parse(value);
updateByQty();
});
}
} else {
setState(() {
});
}
},
),
This is my trying, but can't do that I want.
Please check below method. I think this will resolve your issue. If still not work, please let me know
Widget getTextField({required int maxValue}) {
return TextFormField(
controller: _textController,
keyboardType: TextInputType.number,
onChanged: (text) {
if (int.parse(text) > maxValue) {
// show popup here.
_textController.text = validText;
_textController.selection = TextSelection.fromPosition(TextPosition(offset: _textController.text.length));
}else{
validText = text;
}
},
);
}
As an exercise for my own learning I gave it a go and came up with the following approach; creating a bespoke widget which admittedly looks like a lot of code for something so simple... but it appears to work as expected I think and one could modify, expand and integrate it with other elements in various ways.
Usage: const MaxIntField(max: 100),
Implementation:
class MaxIntField extends StatefulWidget {
const MaxIntField({Key? key, this.max = 1}) : super(key: key);
final int max;
#override
State<MaxIntField> createState() => _MaxIntFieldState();
}
class _MaxIntFieldState extends State<MaxIntField> {
final TextEditingController _controller = TextEditingController();
#override
void initState() {
super.initState();
_controller.value.copyWith(text: '0');
_controller.addListener(() {
if (_controller.text.isNotEmpty && _controller.text != '0') {
int intVal = int.parse(_controller.text);
if (intVal > widget.max) {
setState(() {
_controller.value =
_controller.value.copyWith(text: widget.max.toString());
_showMyDialog();
});
} else if (_controller.text != intVal.toString()) {
//remove leading '0'
setState(() {
_controller.value =
_controller.value.copyWith(text: intVal.toString());
});
}
}
});
}
// assuming using Material
_showMyDialog() async {
showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('AlertDialog Title'),
content: Text('This field is limited to ${widget.max}'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return TextFormField(
controller: _controller,
keyboardType: TextInputType.number,
);
}
}

How to make Speech To Text stop when the user stop talking?

I'm trying to make an icon in the textfield that is supposed to stop and convert into mic_none when the user has finished saying the text , but that it doesn't happen.
What happens is that the text reception stops, but the icon does not return to its outliend form, but rather I have to click on it to convert it into mic_none.
I would appreciate any help from you
TextEditingController? _directionController;
String text = "";
bool isListening = false;
bool isListening1 = false;
#required
Function(String text)? onResult;
#required
ValueChanged<bool>? onListening;
static final _speech = SpeechToText();
void toggleRecording() async {
if (!isListening1) {
bool isAval = await _speech.initialize(
onStatus: (status) => onListening!(_speech.isListening),
onError: (e) => print('Error: $e'),
);
if (isAval) {
setState(() {
isListening1 = true;
});
// it is for recognaization
_speech.listen(
onResult: (value) => setState(() {
_directionController!.text = value.recognizedWords;
addRecipe.userDirections[widget.index] =
value.recognizedWords;
// onResult!(value.recognizedWords);
}));
}
} else {
setState(() {
isListening1 = false;
_speech.stop();
});
}
}
Here is the build of my code:
Widget build(BuildContext context) {
// run this method when the interface has been loaded
WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
_directionController!.text = addRecipe.userDirections[widget.index] ?? '';
});
return TextFormField(
controller: _directionController,
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: toggleRecording,
icon: Icon(
isListening1 ? Icons.mic : Icons.mic_none,
color: Color(0xFFeb6d44),
),
),
hintText: 'Enter a direction'), // errorText: _errorText
onChanged: (value) {
addRecipe.userDirections[widget.index] = _directionController!.text;
// setState(() {}); //used to refresh the screen //OLD
},
validator: (value) {
if (value!.trim().isEmpty) return 'Please enter a direction';
return null;
},
);
}
You can use _speech.isListening instead of your isListening1
Icon(
_speech.isListening? Icons.mic : Icons.mic_none,
color: Color(0xFFeb6d44),
),

How to get error message of validator from TextFormField

I want to get error message programmatically to display somewhere else. Example error show Text is too short, I can get it by using _formKey.currentState... . How to get there?
Form(
key: _formKey,
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
if (value.length < 5 ) {
return 'Text is too short';
}
return null;
},
),
)
Add a variable and reassign the value from your validator:
String? errorMessage;
void saveForm() {
final isValid = _formKey.currentState!.validate();
// If isValid == true > process the save logic, else show error dialog e.g.
if (!isValid) {
// show dialog
return;
}
_formKey.currentState!.save();
// Use your saved data
}
Use the validator return value to perform your save logic conditionally:
Form(
key: _formKey,
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
errorMessage = 'Please enter some text';
return false;
}
if (value.length < 5 ) {
errorMessage = 'Text is too short';
return false;
}
return null;
},
),
)
Inside your widget tree:
final isValid = _formKey.currentState!.validate();
isValid ? SizedBox() : Text(errorMessage)

the argument type 'JsObject cant be assigned to the parameter type 'BuildContext '

hi guys so i was trying to add some error check code in my page 'product_edit' to handle any potential http response error
so this is my page
product_edit.dart
==============================
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import '../models/product.dart';
import '../scoped-models/main.dart';
class ProductEdit extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ProductEditState();
}
}
class _ProductEditState extends State<ProductEdit> {
final Map<String, dynamic> _formData = {
'title': null,
'description': null,
'price': null,
'image':
'https://cdn.pixabay.com/photo/2020/03/04/07/28/black-4900736_960_720.jpg'
};
//String _title;
// String _description;
// double _price;
final GlobalKey<FormState> _formkey = GlobalKey<FormState>();
Widget _buildtypetextfield(Product product) {
return TextFormField(
decoration: InputDecoration(labelText: 'Product Type'),
initialValue: product == null ? '' : product.title,
validator: (String value) {
if (value.isEmpty) {
return 'product type and name is required';
}
return null;
},
onSaved: (String value) {
_formData['title'] = value;
},
);
}
Widget _builddescriptiontextfield(Product product) {
return TextFormField(
maxLines: 6,
decoration: InputDecoration(labelText: 'Description'),
initialValue: product == null ? '' : product.description,
validator: (String value) {
if (value.isEmpty || value.length < 10) {
return 'product description is required,and should be 10+ characters long';
}
return null;
},
onSaved: (String value) {
_formData['description'] = value;
},
);
}
Widget _buildpricetextfield(Product product) {
return TextFormField(
keyboardType: TextInputType.number,
decoration: InputDecoration(labelText: 'Price'),
initialValue: product == null ? '' : product.price.toString(),
validator: (String value) {
if (value.isEmpty ||
!RegExp(r'^(?:[1-9]\d*|0)?(?:\.\d+)?$').hasMatch(value)) {
return 'product price is required,and should be a number';
}
return null;
},
onSaved: (String value) {
_formData['price'] = double.parse(value);
},
);
}
Widget _buildSubmitButton() {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
return model.isloading
? Center(child: CircularProgressIndicator())
: RaisedButton(
child: Text('Save'),
textColor: Colors.white,
onPressed: () => _submitform(
model.addproduct,
model.updateproduct,
model.selectproduct,
model.selectedProductIndex),
);
},
);
}
Widget _buildPageContent(BuildContext context, Product product) {
final double deviceWidth = MediaQuery.of(context).size.width;
final double targetWidth = deviceWidth > 550.0 ? 500.0 : deviceWidth * 0.95;
final double targetPadding = deviceWidth - targetWidth;
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Container(
margin: EdgeInsets.all(10.0),
child: Form(
key: _formkey,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: targetPadding / 2),
children: <Widget>[
_buildtypetextfield(product),
_builddescriptiontextfield(product),
_buildpricetextfield(product),
SizedBox(
height: 10.0,
),
_buildSubmitButton(),
],
),
),
),
);
}
void _submitform(
Function addproduct, Function updateproduct, Function setSelectedProduct,
[int selectedproductIndex]) {
if (!_formkey.currentState.validate()) {
return;
}
_formkey.currentState.save();
//final Map<String, dynamic> product = {
// 'title': _title,
// 'description': _description,
// 'price': _price,
// 'image':
// 'https://cdn.pixabay.com/photo/2020/03/04/07/28/black-4900736_960_720.jpg'
// };
if (selectedproductIndex == -1) {
addproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((_) => Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null)));
} else {
updateproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((_) => Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null)));
}
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
final Widget pageContent =
_buildPageContent(context, model.selectedproduct);
return model.selectedProductIndex == -1
? pageContent
: Scaffold(
appBar: AppBar(
title: Text('Edit Product'),
),
body: pageContent,
);
},
);
}
}
================
and the error occurred in the '_submitform' function block as i was typing the argument context in the show dialog widget
it's a capture of the error
and here is the function , thank u all very much, thanks in advance
_submitform
void _submitform(
Function addproduct, Function updateproduct, Function setSelectedProduct,
[int selectedproductIndex]) {
if (!_formkey.currentState.validate()) {
return;
}
_formkey.currentState.save();
//final Map<String, dynamic> product = {
// 'title': _title,
// 'description': _description,
// 'price': _price,
// 'image':
// 'https://cdn.pixabay.com/photo/2020/03/04/07/28/black-4900736_960_720.jpg'
// };
if (selectedproductIndex == -1) {
addproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((bool success) {
if (success) {
Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null));
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('something went wrong'),
content: Text('please try again!'),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('Okay'),
)
],
);
});
}
});
} else {
updateproduct(_formData['title'], _formData['description'],
_formData['image'], _formData['price'])
.then((_) => Navigator.pushReplacementNamed(context, '/products')
.then((_) => setSelectedProduct(null)));
}
}
showDialog and Navigator requires BuildContext. In your _submitform function add a parameter : BuildContext.
void _submitform(BuildContext context,
Function addproduct, Function updateproduct, Function setSelectedProduct,
[int selectedproductIndex]) {
// code
}
Pass BuildContext here in _submitform function and use that context in showDialog and Navigator. Again you are using _submitform function inside _buildSubmitButton function. So again add a BuildContext parameter in _buildSubmitButton function and pass it to _submitform function and so on. Ultimately you have to pass BuildContext of build function to the _submitform function. This is the flow as per your current code.

How to clear error message in TextFormField in Flutter

In my code I validate phone number. If phone number is incorrect - I show error message. But, when user starts to edit number I want to hide this error message.
I've found the solution with currentState.reset(), but it seems not the good one. I have to handle issues with saving text and cursor position. And I still have one small artifact. Normally when I press and hold backspace - it deletes symbols one by one. If I do it when error message is shown - then error message disappears and only one symbol is deleted.
Does anybody know the right solution for this case?
final TextEditingController controller = TextEditingController();
final RegExp _phoneRegex = RegExp(r"^\+{1}\d{10, 15}\$");
bool isError = false;
TextSelection currentPosition;
return Column(
children: <Widget>[
Form(
key: _textKey,
child: TextFormField(
controller: controller,
validator: (str) {
isError = true;
if (str.isEmpty) {
return err_empty_field;
} else if (!_phoneRegex.hasMatch(str)) {
return err_invalid_phone;
}
isError = false;
},
),
onChanged: () {
if (controller.selection.start < 0 &&
controller.text.length > 0) {
TextSelection position =
controller.text.length > currentPosition.start
? currentPosition
: TextSelection.fromPosition(
TextPosition(offset: controller.text.length));
controller.selection = position;
}
if (isError) {
isError = false;
currentPosition = controller.selection;
if (currentPosition.start > controller.text.length) {
currentPosition = TextSelection.fromPosition(
TextPosition(offset: controller.text.length));
}
String currentText = controller.text;
_textKey.currentState.reset();
controller.text = currentText;
controller.selection = currentPosition;
}
},
),
RaisedButton(
onPressed: () {
_textKey.currentState.validate();
},
child: Text(login),
)
],
);
EDIT (Nov 2020)
autovalidate was deprecated after v1.19.0.
Instead use autovalidateMode:
Form(
autovalidateMode: AutovalidateMode.onUserInteraction`.
...
)
Original post
here is a suitable solution to this problem.
You don't actually need to use onChanged or any tips causing side-effects, I solved it by creating a class property which is initialized to false:
bool _autovalidate = false;
The Form Widget has a named property autovalidate. You should pass it the previous boolean:
Form(
key: _textKey,
autovalidate: _autovalidate,
...
)
And in your Submit button onPressed() method, you should update the _autovalidate boolean to be true if the form is invalid, this will make the form to auto validate the TextFormField on every onChanged call:
RaisedButton(
onPressed: () {
if (_textKey.currentState.validate()) {
print('valid');
} else {
print('invalid');
setState(() => _autoValidate = true);
}
},
child: Text(login),
)
I hope it helped Somebody.
January 2021
...
AutovalidateMode _autoValidate = AutovalidateMode.disabled;
Form(
key: _textKey,
autovalidateMode: _autovalidate,
...
)
RaisedButton(
onPressed: () {
if (_textKey.currentState.validate()) {
print('valid');
} else {
print('invalid');
setState(() => _autoValidate = AutovalidateMode.always);
}
},
child: Text("login"),
)
The problem here is errorText is automatically managed by the validator field of the TextFormField. At the same time, the simple solution is to handle the errorText manually.
Step 1: Create
String field, _errorText initialised to null. The field will hold the error message that needs to be shown.
Boolean field, _error initialised to false. The filed is true if there is an error otherwise false.
Step 2:
Assign _errorText to TextFormField
Step 3 (Important):
Make sure that TextFormField validator returns a null value.
Handle the validation here and assign the proper error message to _errorText.
Update _error state correspondingly.
Step 4 (Important):
Reset _errorText and _error. This will remove the error from field soon as you start editing.
Step 5:
Trigger field validation in the onFieldSubmitted and manage your code flow...
import 'package:flutter/material.dart';
class WorkGround extends StatefulWidget {
#override
_WorkGroundState createState() => _WorkGroundState();
}
class _WorkGroundState extends State<WorkGround> {
final _formKey = GlobalKey<FormState>();
final _usernameFocusNode = FocusNode();
final _phoneNumberFocusNode = FocusNode();
/*
* Step 1.
* */
String _userNameErrorText;
bool _userNameError = false;
String _phoneNumberErrorText;
bool _phoneNumberError = false;
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextFormField(
focusNode: _usernameFocusNode,
decoration: InputDecoration(
labelText: 'Username',
/*
* Step 2
* */
errorText: _userNameErrorText, // Handling error manually
),
textInputAction: TextInputAction.next,
/*
* Step 3
* */
validator: (value) {
setState(() {
if(value.isEmpty) {
_userNameError = true;
_userNameErrorText = 'Enter Username';
}
});
return null; // Return null to handle error manually.
},
/*
* Step 4
* */
onChanged: (value) {
setState(() {
_userNameError = false;
_userNameErrorText = null; // Resets the error
});
},
/*
* Step 5
* */
onFieldSubmitted: (value) {
_formKey.currentState.validate(); // Trigger validation
if(!_userNameError) {
FocusScope.of(context).requestFocus(_phoneNumberFocusNode);
}
},
),
TextFormField(
focusNode: _phoneNumberFocusNode,
decoration: InputDecoration(
labelText: 'Phone Number',
/*
* Step 2
* */
errorText: _phoneNumberErrorText, // Handling error manually
),
textInputAction: TextInputAction.done,
/*
* Step 3
* */
validator: (value) {
setState(() {
if(value.isEmpty) {
_phoneNumberError = true;
_phoneNumberErrorText = 'Enter Phone number';
} else if( value.length < 10) {
_phoneNumberError = true;
_phoneNumberErrorText = 'Invalid Phone number';
}
});
return null; // Return null to handle error manually.
},
/*
* Step 4
* */
onChanged: (value) {
setState(() {
_phoneNumberError = false;
_phoneNumberErrorText = null; // Resets the error
});
},
/*
* Step 5
* */
onFieldSubmitted: (value) {
_formKey.currentState.validate(); // Trigger validation
if(!_phoneNumberError) {
// submit form or whatever your code flow is...
}
},
),
],
),
),
);
}
}
I have achieved your both below functionality:
1) Hide error message when editing
2) validate input field when login button pressed
Note: i have commented phone number regex and put validation for
string length < 10 digit for testing.
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState(){
super.initState();
}
final TextEditingController controller = TextEditingController();
// final RegExp _phoneRegex = RegExp(r"^\+{1}\d{10, 15}\$");
bool isError = false;
bool isWriting = false;
bool isLoginPressed = false;
int counter = 0;
String myErrorString = "";
TextSelection currentPosition;
final _textKey = GlobalKey<FormState>();
#override
Widget build(BuildContext ctx) {
return Scaffold(
appBar: AppBar(
title: Text('MapSample'),
),
body: Container(
child: Column(
children: <Widget>[
Form(
key: _textKey,
child: TextFormField(
controller: controller,
validator: (str) {
myErrorString = "";
if(isLoginPressed){
isError = true;
if (str.isEmpty) {
myErrorString = 'err_empty_field';
return myErrorString;
}
else if (str.length < 10) {
myErrorString = 'err_invalid_phone';
validateMe();
return myErrorString;
}
/*else if (!_phoneRegex.hasMatch(str)) {
myErrorString = 'err_invalid_phone';
validateMe();
return myErrorString;
}*/
isError = false;
myErrorString = "";
}else{
myErrorString = "";
}
},
),
onChanged: () {
counter++;
if(counter == 9){
counter = 0;
isLoginPressed = false;
}
if(isLoginPressed){
}else{
isWriting = true;
isLoginPressed = false;
myErrorString = "";
_textKey.currentState.validate();
}
},
),
RaisedButton(
onPressed: () {
counter = 1;
isWriting = false;
isLoginPressed = true;
_textKey.currentState.validate();
},
child: Text('login'),
)
],
),
),
);
}
void validateMe() {
if(isLoginPressed){
currentPosition = TextSelection.fromPosition(
TextPosition(offset: controller.text.length));
String currentText = controller.text;
_textKey.currentState.reset();
controller.text = currentText;
controller.selection = currentPosition;
isWriting = false;
isLoginPressed = true;
}
}
}
I've found working and easier way
final _textKey = GlobalKey<FormState>();
final TextEditingController _controller = TextEditingController();
Widget _getPhoneInputForm() {
final RegExp _phoneRegex = RegExp(r"^\+{1}\d{10,17}");
bool isError = false;
bool isButtonPressed = false;
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 36.0),
child: Form(
key: _textKey,
child: TextFormField(
keyboardType: TextInputType.phone,
decoration: InputDecoration(
hintText: hint_enter_phone,
contentPadding: EdgeInsets.all(24.0),
fillColor: Colors.blueGrey.withOpacity(0.3),
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(16.0)),
borderSide: BorderSide(color: Colors.blueGrey))),
controller: _controller,
validator: (str) {
if (!isButtonPressed) {
return null;
}
isError = true;
if (str.isEmpty) {
return err_empty_field;
} else if (!_phoneRegex.hasMatch(str)) {
return err_invalid_phone;
}
isError = false;
},
onFieldSubmitted: (str) {
if (_textKey.currentState.validate()) _phoneLogin();
},
),
onChanged: () {
isButtonPressed = false;
if (isError) {
_textKey.currentState.validate();
}
},
),
),
RaisedButton(
color: Colors.teal,
textColor: Colors.white,
onPressed: () {
isButtonPressed = true;
if (_textKey.currentState.validate()) _phoneLogin();
},
child: Text(login),
)
],
);
}
This is an exemple , i think its not necessary to do onchange() , the function validate name do the work ...
String validateName(String value) {
String patttern = r'(^[a-zA-Z ]*$)';
RegExp regExp = new RegExp(patttern);
if (value.length == 0) {
return "Name is Required";
} else if (!regExp.hasMatch(value)) {
return "Name must be a-z and A-Z";
}
return null;
}
TextFormField(
controller: _lastname, validator: validateName ,
//initialValue: widget.contact.last_name,
decoration:
InputDecoration(labelText: 'Last name'),
),
void Save() {
if (_keyForm.currentState.validate()) {
// No any error in validation
_keyForm.currentState.save();
................
}
i have found that using a combination of FocusNode and AtuoValidateMode.onUserInteraction does the trick.
class _TextAutoValidateModeExampleState extends State<TextAutoValidateModeExample> {
FocusNode node = FocusNode();
#override
Widget build(BuildContext context) {
return Container(
child: TextFormField(
focusNode: node,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if(node.hasFocus) return null;
if (value!.isEmpty) return "value cannot be empty";
if (!value.isEmail) return "not a valid email";
},
),
);
}
}
// Call this method inside onChanged() and when its focusnode hasFocus
void formReset(GlobalKey<FormState> formKey, TextEditingController controller) {
String stringValue = controller.text;
TextPosition textPosition = controller.selection.base;
formKey.currentState.reset();
controller.text = stringValue;
controller.selection = TextSelection.fromPosition(textPosition);
}
this format worked for me, Hope it helps someone....
validator: (value){
bool emailValid = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+#[a-zA-Z0-9]+\.[a-zA-Z]+").hasMatch(value);
isError = true;
if(value.isEmpty){
return "Provide an email";
}else if(!emailValid){
return "Enter a valid email";
}
isError = false;
return null;
},