Error when supplying an initial value to TextField - flutter

I am facing a problem with TextField, since I am working hand in hand with TextEditingController()..text and onChanged, but when entering a new data, it is not reflected in the TextField. I made a print inside the onChanged this same one recognizes a new entry but the value to initiate continues without being updated. Inside the onChanged, I have a function which is in charge of validating what is entered and returning an error if necessary. When I comment the instruction before mentioned the TextField already allows to enter and to update what the user enters.
I hope you can help me, a feedback, tutorial, etc.
I would appreciate it.
TextField Code:
CustomTextField(
controller: TextEditingController()..text = datumAdministrative.name,
placeholder: Constants.selectDate,
helperText: Constants.requiredData,
keyboardType: TextInputType.text,
enable: true,
errorText: validationForm.name.error,
textInputAction: TextInputAction.next,
textCapitalization: TextCapitalization.sentences,
onChanged: (String value) {
validationForm.changeName(value);
},
);
ValidationForm Code:
void changeName(String value) {
String pattern = r'(^[a-zA-Z ]*$)';
RegExp regExp = new RegExp(pattern);
regExp.hasMatch(value)
? _name = ValidationItem(value, null)
: _name = ValidationItem(null, Constants.nameAdministrativeMessage);
notifyListeners();
}

Try Using : TextController(text: "<Required Text>")
Also does the validation have to be every single time the user enter any word ?
If not, you can try validation everything at the end.
If you are trying to use reactive validation, make sure your CustomTextField is wrapped with the widget which is responsible for rebuilding the UI.. something like Consumer() when using provider package

The problem is probably caused because the TextEditingController object is getting discarded by the rebuilds made by Flutter because you are instantiating the TextEditingController inside a build method. You should save the instance of your controller elsewhere, like in a state object as shown by the official docs or in your case, you can create it and get it from your validationForm.
This is the example in the docs:
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
/// save the [TextEditingController] instance
final TextEditingController _controller = TextEditingController();
...
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(6),
child: TextFormField(
controller: _controller, // User your instance
decoration: const InputDecoration(border: OutlineInputBorder()),
),
),
);
}
}
Possible solution in your case:
CustomTextField(
controller: validationForm.myTextController,
placeholder: Constants.selectDate,
...

Related

Validation in Flutter

I want to know how to make a validation in flutter where IF a TextFormField is filled then when you hit "send" then it doesn't let you go to the next section until all other textformfields must be filled, BUT if one of the TextFormFields is NOT filled when you hit send then it lets you pass to the next section. This is for a job form where a section is NOT mandatory, but only if one field has been filled then it becomes mandatory.
If you have a Form widget that contains all your FormFields (not only text-ones, but also dropdowns and such), the validation occurs on all your fields at once if you write your submit code this way:
final _formKey = GlobalKey<FormState>();
var tecUser = TextEditingController();
var tecPwd = TextEditingController();
[...]
//inside your widget tree...
Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: tecUser,
validator: (value) {
//your validation code: return null when value is right
//or a string if there's some error
},
decoration: InputDecoration(hintText: "username".tr()),
),
const SizedBox(height: 10),
TextFormField(
controller: tecPwd,
validator: (value) {
//your validation code: return null when value is right
//or a string if there's some error
},
obscureText: true,
),
const SizedBox(height: 10),
OutlinedButton(child: const Icon(Icons.login), onPressed: () => _submit()),
[...]
void _submit() async {
if (_formKey.currentState!.validate()) {
//all validators returned null, so you can proceed with your logic
} else {
//this happens when at least one of the validators returned a string
//by default, the error string returned by the validators will be displayed
//near each field, so you won't have to worry about handling the error cases and the else here won't even be necessary
}
}
This is an excerpt from an actual login form.
EDIT:
Ok, now I understand what you want to do. You have a group of fields that aren't mandatory, but they instead are mandatory if at least one of them has some value.
You need to assign a different TextEditingController to each of this fields: then, you need to assign a validator to each FormField that does something like this:
//insert all the TextEditingController in a list
var tecList = <TextEditingController>[tec1, tec2...]
//then, set a validator like this
(value) {
bool notMandatory = true;
for (var tec in tecList){
notMandatory = notMandatory && tec.text.isEmpty;
}
if (!notMandatory) return "Fill all fields";
//some other validation here
}
If you use a TextEditingController you can use the .text.isNotEmpty statement an write yourself a litte if function to check everything.
TextEditingController controller = TextEditingController();
if (controller.text.isNotEmpty) {
print("have fun with your new job")
}

How to make provider listener listen only once

I am geting input(text) using textfield and displaying in a list, I am using onchanged property of textfield and provider to update the text of new element in list but, all elements in the list update to onChanged's new value, once the element is added to list I want it to stop listening to changes of onChanged. So, that I can display list with different elements. How do I achieve that.
TextField(
autofocus: true,
decoration: kTextFieldDecocation.copyWith(
hintText: 'B Name'),
onChanged: (newbName) {
Provider.of<BNameControllerClass>(context,
listen: false)
.newBorrowerName(newbName);
},
),
List element's text
Text(
Provider.of<BNameControllerClass>(context,
listen: true)
.bName,
style: const TextStyle(fontSize: 20),
);
provider class
class BNameControllerClass extends ChangeNotifier {
String bName = 'Ganesh';
newBorrowerName(String newName) {
bName = newName;
notifyListeners();
}
}
Create List<String> textsFromInput = [];
Generate text widgets with ListView.generate().
Then in TextField you can use onSubmitted: (value) => textsFromInput.add(value)
Depending on what state management you have you can then call setState() or handle list rebuilding with bloc builder buildWhen: previous.textsFromInput.length != current.textsFromInput.length or with provider.

Flutter- TextEditingController listener get called multiple time for textfield

TextField controlled controller.addListener(() gets called multiple time after pressing the clear button, this will only happen if we are clearing it.
Snippet:
TextEditingController controller = new TextEditingController();
TextField field = new TextField(
controller: controller,
autofocus: true,
);
controller.addListener(() {
print("Pressed cancel button");
});
Video Link
Note: While adding characters in TextField listener method gets called only ones.
I guess that would be a defect on flutter, a possible solution would be to use onChanged()
TextField field = new TextField(
autofocus: true,
onChanged: (String value) {
print("Pressed clear button");
},
);
I have the same problem with Nexus 6p when used with API level 23 and Pixel with API 25.
but this problem did not occurs with Pixel with API28 and it does not occurs with Nexus6P with API26.
exact code from https://flutter.dev/docs/cookbook/forms/text-field-changes was used.
1. We need to create our own .clear() method
void clearField() {
print("c: clearField");
var newValue = textController.value.copyWith(
text: '',
selection: TextSelection.collapsed(offset: 0),
);
textController.value = newValue;
callApi('');
}
// and call it by :
child: TextField(
controller: textController,
autofocus: true,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: clearField, // call
),
),
),
2. We need to carefully handle changes
void changesOnField() {
print("c: changesOnField");
String text = textController.text;
if (text.isNotEmpty) { // set this
callApi(text);
}
}
Full Code
You may look into this repo and build it locally Github
Result

disappear of the text written in the text field after pressing done

I am using a text field in my app to write comments, when the user type a text and press a button the text should be written to the database. However, I have a problem which is when I write the text then press done or return from the keyboard, the text disappear then there is no comment to be added. Is there any idea to save the value of the text in the text field even after pressing done or return?
Not sure why your input is disappearing but you can use a TextEditingController and pass that controller to a TextField.
Then access the value of the TextField using controller.text .
Here is a little example
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
String databaseText;
TextEditingController controller = TextEditingController();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: TextField(
controller: controller,
onEditingComplete: () {
databaseText = controller.text;
print(databaseText);
},
),
),
),
);
}
}
this issue happen because the TextEditingController is rebuilt after you add your text. for me I used static controller to solve this problem.
static TextEditingController textController = TextEditingController();
and I highly recommend reading this
String _text;
final formKey = GlobalKey<FormState>();
Form(
key: formKey,
child: Column(children: <Widget>[
TextFormField(
autocorrect: false,
decoration: InputDecoration(labelText: "Text:"),
onSaved: (str) => _text = str,
)
]));
Then just pass your _text variable inside your paramater as your key value.
Try modifing contentPadding in InputDecoration. For example:
contentPadding: EdgeInsets.symmetric(horizontal: 2, vertical: 0)
The corresponding Flutter bug report is #29542: "Text getting cleared when using TextEditingController AND StreamBuilder".
There, user bizz84 commented, asking:
So the reason that this works with StatefulWidget is that the state is retained even across rebuilds, so I can use it to hold my TextEditingControllers.
Is this correct?
And rrousselGit replied:
Exactly :-)
Generally speaking, don't create anything but primitives in a StatelessWidget/InheritedWIdget. For everything else, you'll need a State
So, this appears to be the official answer: if you are creating anything other than primitives, you will need a State.

How to change TextField text from Store with flutter_flux?

In the flutter_flux example when we commit a new message, the _currentMessage is emptied but the TextField does not reflect that changes.
This is the code in the store:
triggerOnAction(commitCurrentMessageAction, (ChatUser me) {
final ChatMessage message =
new ChatMessage(sender: me, text: _currentMessage);
_messages.add(message);
_currentMessage = '';
});
The view uses a TextEditingController as a controller for the TextField Widget so I understand why it is not updated.
How can we empty the TextField from the Store with flutter_flux?
EDIT: The flutter_flux example has been updated since I posted this answer, and it now correctly discards message in the TextField but in a better way. You should check it out.
I think the correct way would be to move the TextEditingController to the ChatMessageStore, instead of simply keeping the currentMessage in that store. Then you would be able to empty the text field by calling clear() on the TextEditingController.
Generally speaking, the state values which would normally be kept in FooState in vanilla flutter would go into a Store when using flutter_flux. Since you would normally create and keep a TextEditingController in a State, I think it's more natural to keep it in a Store anyways.
The updated ChatMessageStore would look something like this:
class ChatMessageStore extends Store {
ChatMessageStore() {
triggerOnAction(commitCurrentMessageAction, (ChatUser me) {
final ChatMessage message =
new ChatMessage(sender: me, text: currentMessage);
_messages.add(message);
_msgController.clear();
});
}
final List<ChatMessage> _messages = <ChatMessage>[];
final TextEditingController _msgController = new TextEditingController();
List<ChatMessage> get messages =>
new List<ChatMessage>.unmodifiable(_messages);
TextEditingController get msgController => _msgController;
String get currentMessage => _msgController.text;
bool get isComposing => currentMessage.isNotEmpty;
}
Note that we no longer need the setCurrentMessageAction, as the TextEditingController would take care of the text value change itself.
Then, the msgController defined in ChatScreen widget could be removed and the _buildTextComposer could be updated accordingly.
Widget _buildTextComposer(BuildContext context, ChatMessageStore messageStore,
ChatUserStore userStore) {
final ValueChanged<String> commitMessage = (String _) {
commitCurrentMessageAction(userStore.me);
};
ThemeData themeData = Theme.of(context);
return new Row(children: <Widget>[
new Flexible(
child: new TextField(
key: const Key("msgField"),
controller: messageStore.msgController,
decoration: const InputDecoration(hintText: 'Enter message'),
onSubmitted: commitMessage)),
new Container(
margin: new EdgeInsets.symmetric(horizontal: 4.0),
child: new IconButton(
icon: new Icon(Icons.send),
onPressed:
messageStore.isComposing ? () => commitMessage(null) : null,
color: messageStore.isComposing
? themeData.accentColor
: themeData.disabledColor))
]);
}
Hope this helps.