Show element as soon as form is valid - flutter

I'm trying to show element as soon as my form becomes valid. According to flutter docs I need to use _formKey.currentState.validate() but in the docs it is used on button click, while I'm trying to use it to show/hide element.
Working code (as used in flutter tutorial)
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
autovalidate: true,
validator: validateName,
),
RaisedButton(
onPressed: () => _formKey.currentState.validate()
? print('valid')
: print('not valid'))
My code with (INVALID MEMER OF NULL: 'validate) error
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
autovalidate: true,
validator: validateName,
),
_formKey.currentState.validate()
? Container(child: Text('valid'))
: Container(child: Text('not valid'))

The best answer for you will be to use TextFormField onChanged, which is of type ValueChanged, which keeps track of the changes you make in your TextFormField
Advantage
You will be able to keep a track of the text changes, and show/hide content based upon that
Disadvantage
Each TextField should be maintaining the bool for the different TextField to keep a track of the items
It is like a workaround, so can use in this way. I am using a single TextFormField to show how it works, you can then work on ther TextFormField and get it up and running.
FINAL SOLUTION
bool isValid = false;
final TextEditingController _controller = new TextEditingController();
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: InputDecoration(hintText: 'Enter your data'),
controller: _controller,
onChanged: (String val){
setState((){
if(val.length >= 4) isValid = true;
else isValid = false;
});
}
),
SizedBox(height: 20.0),
isValid ? Text('Valid String', textAlign: TextAlign.center) : Text('Name must be more than 3 character', textAlign: TextAlign.center)
]
)
SOLUTION: For various, you must check whether all the different bools are true or not, based upon that, you can show/hide the data.
RESULT
Let me know if is of any use to you :)

Related

TextField stops after writing 1 letter flutter

I am trying to modify a TextField inside an ExpansionTile title and when I modify the text I want to update data in my database firestore, this part works fine. The problem is when I write something in my TextField I can only write one letter and then I have to select the TextField again.Initial text field value
Second text field value
In the second picture after writing the number 2 in test the 'blue mark' exits the TextField and if I want to write in the TextField again I have to select the text field again.
This is the TextField code inside the expansion tile, all this code is inside a stream where each list tile is an entry from the database:
return SizedBox(
width: double.infinity,
child: SingleChildScrollView(
child: Column(
children: [
Column(
children: [
ListView.builder(
physics: const ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: widget.shoppingListsCart.length,
itemBuilder: (BuildContext context, int index) {
String shoppingCartListName =
widget.shoppingListsCart.elementAt(index).name;
final shoppingListId =
widget.shoppingListsCart.elementAt(index).shoppingListId;
TextEditingController _textEditingControllerShoppingListName = TextEditingController();
_textEditingControllerShoppingListName.text = shoppingCartListName;
_textEditingControllerShoppingListName.addListener(() {
widget.firebaseCloudStorage.updateShoppingListName(
widget.shoppingCartListId, shoppingListId, _textEditingControllerShoppingListName.text);
});
return Column(
children: [
ExpansionTile(
backgroundColor: Colors.indigo,
title: SizedBox(
child: Row(
children: [
SizedBox(
width: 220,
child: TextField(
decoration: const InputDecoration(
border: InputBorder.none,
counterText: '',
),
controller: _textEditingControllerShoppingListName,
maxLength: 35,),
), ),
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconButton(onPressed: (){
}, icon:Icon(Icons.edit,)),
IconButton(onPressed: () {}, icon: Icon(Icons.delete)),
],
),
]),
),
children: [
ProductsInShoppingListView(
shoppingListName: shoppingCartListName,
shoppingCartId: widget.shoppingCartListId,
shoppingListId: shoppingListId,
firebaseCloudStorage: widget.firebaseCloudStorage,
),
]),
],
);
},
),
Column(
children: [
ProductsInCartView(
shoppingCartId: state.shoppingCartId),
],
),
],
),],
),
),
);
I tried using a Future in the text field and changing from a stateless widget to a stateful widget, the error is something like this TextField accepts only one letter and loses focus after each letter but since I am using flutter that solution didn't work for me. I don't know what is the error because the console doesn't say anything. Please help, thank you.
did you try it? It's TextFormField. Here I fixed the value 1, we can make it a dynamic value. But I hope this will be helpful to think about next.
TextFormField(
onChanged: (value) {
if (value.length == 1) {
FocusScope.of(context).nextFocus(); //whatever you want to do
}
},
inputFormatters: [
LengthLimitingTextInputFormatter(1)
],
),

TextFormField retrieves the focus and reassigns the initial value when the keyboard is closed

I find myself working with TextFormField hand in hand with DropdownButton. There is a strange process going on inside the view. I put the steps below:
To the TexFormField, I'm assigning an initial value: controller: _nameController..text = datumAdministrative.name.
I enter a new value to the TextFormField.
When the DropdownButton is deployed, the keyboard is closed the whole Widget is redrawn, which causes the TextFormField to recover its initial value and does not keep the new value entered.
Is there any way to avoid, that the TextFormField returns to its initial value when selecting DropdownButton? I would appreciate if you could help me with a post or feedback.
Code Scaffold:
return Scaffold(
resizeToAvoidBottomPadding: false,
resizeToAvoidBottomInset: true,
appBar: AppBar(
backgroundColor: ColorsTheme.primary,
leading: GestureDetector(
onTap: () {
Navigator.pushReplacementNamed(context, 'administrativeListData');
},
child: Container(
padding: EdgeInsets.all(16),
child: FaIcon(FontAwesomeIcons.arrowLeft)),
),
centerTitle: true,
title: Text(datumAdministrative.name +
' ' +
datumAdministrative.lastNameFather),
automaticallyImplyLeading: false),
body: Stack(
children: <Widget>[
ImageBackground(),
_sizeBoxHeight(),
Container(
child: Form(
key: formValidator.formKey,
child: ListView(
children: <Widget>[
_containerImageUser(context, datumAdministrative),
_sizeBoxHeight(),
CustomCardExpansionTile(
title: Constants.administrativeData,
icon: FontAwesomeIcons.addressBook,
widget: Container(
padding: EdgeInsets.all(15),
child: responsiveDataAdministrative(context)),
),
CustomCardExpansionTile(
title: Constants.addressInformationAdministrative,
icon: FontAwesomeIcons.houseUser,
widget: Container(
padding: EdgeInsets.all(15),
child: responsiveAddressInformationAdministrative(
context))),
CustomCardExpansionTile(
title: Constants.userDataSystem,
icon: FontAwesomeIcons.idCard,
widget: Container(
padding: EdgeInsets.all(15),
child: responsiveInformationSystemAdministrative(
context))),
],
),
),
),
],
),
);
Code TextFormField:
TextFormField(
controller: _nameController..text = datumAdministrative.name,
keyboardType: TextInputType.text,
textInputAction: null,
textCapitalization: TextCapitalization.sentences,
onChanged: (value) => formValidator.name = value,
validator: (value) {
if (formValidator.fieldValidText(value)) {
return null;
} else {
return Constants.nameAdministrativeMessage;
}
});
I see that you set the value for the text property of TextEditingController inside the build method. So, it will be invoked whenever the widget rebuilds, causing the value for the field back to the initial one.
The docs actually tells about it:
text property
Setting this will notify all the listeners of this
TextEditingController that they need to update (it calls
notifyListeners). For this reason, this value should only be set
between frames, e.g. in response to user actions, not during the
build, layout, or paint phases.
To fix it, you should remove the cascade notation here:
controller: _nameController..text = datumAdministrative.name,
Do it inside initState() instead:
#override
void initState() {
_nameController.text = datumAdministrative.name;
}

Remove focus from TextField when the keyboard is dismissed

It seems that a TextField does not lose the focus when the keyboard is dismissed. That makes the cursor continually blink, and more seriously, when the main window gets focus again, for example, after showing a dialogue box, the keyboard pops up again. Can I make the TextField lose focus when the keyboard is dismissed?
return Scaffold(
body: Padding(
padding: EdgeInsets.all(16),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(decoration: InputDecoration(hintText: "Example"),),
RaisedButton(
child: Text("Test"), onPressed: () => onButtonClick(context))
],
),
)),
);
Try using this when clicking on the button:
FocusScope.of(context).unfocus();
and if you have a textEditingController (which I recommend using) you can call to clear the field
_textEditingController.clear();
but you have to create the controller first:
final _textEditingController = TextEditingController();
and when you create your TextField:
TextField(
decoration: InputDecoration(hintText: "Example"),
controller: _textEditingController,
),
Use TextEditingController:
First Initialise :
TextEditingController _textController = new TextEditingController();
code:
TextField(controller: _textController),

Control position of validation label

When using Form validator the default position of the message label is on the bottom, is there a way to put it on the top of the Text field?
You can not change position on validation label in TextFormField
But you can build your own TextFormField by using FormField widget and custumize its children to look like a normal TextFormField this is a working example made with FormField
FormField(
// initialValue: 0,
autovalidate: true,
validator: normalValidator,
builder: (state) {
state.validate();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
state.errorText!=null? Text(state.errorText, style: TextStyle(color: Colors.red, fontSize: 12),):Container(),
DropdownButton(
isExpanded: true,
value: state.value,
items: _choices,
onChanged: (v) {
state.didChange(v);
setState((){});
}),
],
);
}),
You can now customize it to sweet your needs. Find more about FormField here on official doc

How to customize the layout of a validate message error in a flutter form

i'm trying to do something like this
this is my Form
Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(
vertical: 16.0, horizontal: 30.0),
child: TextFormField(
controller: _controllerEmail,
maxLines: 1,
decoration: InputDecoration(
hintText: "Enter your E-mail",
labelText: "E-mail",
),
// ignore: missing_return
validator: (text) {
if (text.isEmpty) {
return 'E-mail is required';
}
},
),
),
this is what i'm getting the default error message,anyway to change that?
TLDR : not with the InputDecoration, Form, FormField framework to validate and display error.
The beauty of Flutter being open source is that you can check the source code for yourself to get the idea. Here is the widget that displays the errorText. The only modification you can do it changing the style using errorStylein InputDecoration.
If you really want to achieve something like the one mentioned in your question, I suggest you use bubble package to build that error bubble and separate mechanism to validate the form.