I'm having a weird issue with a TextFormField, it cuts off the last input. If I type something like hello, it's gonna cut off the o and save hell. if I give it a space like hello , it's gonna save hello. Has anyone been through the same issue and know how to fix it?
here is my code:
String messageText;
_ChatScreenState() {
_formKey = GlobalKey<FormState>();
_scrollController = ScrollController();
messageText = "";
}
Widget messageTextField() {
return SizedBox(
width: deviceWidth * 0.55,
child: TextFormField(
style: TextStyle(color: Colors.white),
validator: (text) {
if (text.isEmpty) {
return 'Digite sua mensagem';
}
return null;
},
onChanged: (text) {
_formKey.currentState.save();
},
onSaved: (text) {
setState(() {
messageText = text;
});
},
cursorColor: Colors.white,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Digite uma mensagem',
hintStyle: TextStyle(color: Colors.white.withAlpha(100))),
autocorrect: false,
),
);
}
I do not think save() is intended to be used with onChanged.
If you place a button somewhere and perform _formKey.currentState.save(); within onPressed of the button, you should see the entire text value being saved.
Related
iam using a device that scans barcode , I want after each read the focus to return the TextFormFiled - the code below do the work and I see the cursor is focused on the TextFormFiled but when I read next time its show nothing , I need to manually just click by my finger on the textfiled to activate the focus ,can somebody help me ( the device returned LF after each read)
TextFormField(
decoration: new InputDecoration(
border: new OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(10.0),
),
),
filled: true,
hintStyle: new TextStyle(
color: Colors.grey[800]),
hintText: "Read BarCode",
fillColor: Colors.white70),
focusNode: myFocusNode,
controller: search,
autofocus: true,
maxLines: null,
validator: (value) {
// print(value.toString().runes);
if (value.toString().contains("\n")) {
fetchProducts(value!);
search.text = "";
} else {}
},
),
Use your myFocusNode to activate the focus on textField.
void function(){
/// after scanning is complete call this
focusNode.requestFocus()
}
I pass for this and did solve it like this:
_addItem() {
final isValid = _formKey.currentState?.validate() ?? false;
if (!isValid) {
return;
}
final ean = int.parse(_eanController.text);
listaEan.add(ean);
_eanController.text = '';
setState(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_eanFocus.requestFocus();
});
});
}
but in physical scan device, did works fine. without use the addPostFramaCallback.
I need separate logic with UI.
I used the following example:
1.- Use a class validation item to show a string value and error.
class ValidationItem {
final String value;
final String error;
ValidationItem(this.value, this.error);
}
2.- Use the next code for provider class.
class SignupValidation with ChangeNotifier {
ValidationItem _firstName = ValidationItem(null,null);
//Getters
ValidationItem get firstName => _firstName;
bool get isValid {
if (_firstName.value != null){
return true;
} else {
return false;
}
}
//Setters
void changeFirstName(String value){
if (value.length >= 3){
_firstName=ValidationItem(value,null);
} else {
_firstName=ValidationItem(null, "Must be at least 3 characters");
}
notifyListeners();
}
void submitData(){
print("FirstName: ${firstName.value}");
}
}
3.- Use the next widget to show text field and validate
class Signup extends StatelessWidget {
#override
Widget build(BuildContext context) {
final validationService = Provider.of<SignupValidation>(context);
return Scaffold(
appBar: AppBar(
title: Text('Signup'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
TextField(
decoration: InputDecoration(
labelText: "First Name",
errorText: validationService.firstName.error,
),
onChanged: (String value) {
validationService.changeFirstName(value);
},
),
RaisedButton(
child: Text('Submit'),
onPressed: (!validationService.isValid) ? null : validationService.submitData,
)
],
),
),
);
}
}
The problem is the performance for example every time the text is changed the notifyListener() Is calles.
My question: Is there a cost to performance?
you can use TextFormField instead of TextField.
Best way to validate fields is that you can use validator property TextFormField property as bellow
TextFormField(
decoration: InputDecoration(
labelText: "First Name",
errorText: validationService.firstName.error,
),
validator: (e){
if(e!.trim().isEmpty) return "String is empty";
return null;
},
onChanged: (String value) {
validationService.changeFirstName(value);
},
),
The TextField itself gives you the ability to validate the form, then why to make it complex by implementing notifier, instead you want to make it common you can make the validater global function for it. Nad user it in the validate function.
Void validateEmail(String value){ // your logic}
Use this function as follow
TextFormField(
decoration: InputDecoration(
labelText: "First Name",
errorText: validationService.firstName.error,
),
validator: validateEmail(),
onChanged: (String value) {
validationService.changeFirstName(value);
},
Secondly to get the value of inputted string you have a TextEditingController which directly give you the string you inputted.
Declare TextEditingController as follow
TextEditingController emailCont = TextEditingController();
Use this controller in TextField as follow
TextFormField(
controller: emailCont,
decoration: InputDecoration(
labelText: "First Name",
errorText: validationService.firstName.error,
),
validator: validateEmail(),
onChanged: (String value) {
validationService.changeFirstName(value);
},
Now to get the value from this controller you can get it this way.
emailCont.text
This way it will be easy to manage and less complexity.
So I have TextFormField and it has a set value. However when I try to change the value of it by typing in the Feild, it just reverts to the specified value. This is my code:
TextFormField(
onEditingComplete: () {
FocusScope.of(context).unfocus();
setState(() {
// THIS IS WHERE I NEED TO CHANGE ITS VALUE
});
},
controller: _titleController..text = newTask.title, //newTask IS AN OBJECT
cursorHeight: 23.0,
decoration: InputDecoration(
disabledBorder: UnderlineInputBorder(),
border: OutlineInputBorder(),
hintText: "Enter Text",
labelText: "Title",
labelStyle: TextStyle(
fontSize: 20.0
),
),
),
I need to somehow set _titleController.text to the value that is in the field when enter or the tick button is clicked - but I cant just do _titleController.text = _titleController.text; for obvious reasons. Please help. Comment if you need more details.
change it to this:
#override
void initState() {
super.initState();
_titleController.text = newTask.title;
}
.
.
.
.
.
//And in your TextFormField
controller: _titleController,
You assign the value of newTask.title to _titleController.text
controller: _titleController..text = newTask.title, //newTask IS AN OBJECT
Make it:
controller: _titleController
I am trying to update a text widget as the user types, its working but i don't see the input as I type
TextFormField(
autofocus: true,
controller: amountController,
keyboardType: TextInputType.number,
cursorColor: Colors.amber,
decoration: InputDecoration(
hintText: "Enter Amount",
prefixIcon: Material(
elevation: 0,
borderRadius: BorderRadius.all(Radius.circular(30)),
child: Icon(
Icons.phone_outlined,
size: 15,
color: Color(0xff020529),
),
),
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(horizontal: 25, vertical: 13)),
onChanged: (value) {
add(value);
},
),
this is the widget
Text( x.toString(),
style: TextStyle(fontWeight: FontWeight.bold),
),
This is my method
add(String value) {
var ans = 36 * double.parse(value);
print('ddd');
setState(() {
x = ans;
});
What's happening is double.parse is failing because the given value can't be converted to a number.
One option to handle this is by using a try catch (see following code sample), and then setting the displayed value to 0 (or could also do something like set an error message saying something like "invalid input" if the user enters something like "123d"
add(String value) {
double ans;
try {
ans = 36 * double.parse(value);
} on FormatException catch (e) {
// string couldn't be formatted to double
ans = 0;
}
setState(() {
x = ans;
});
}
I used a try catch, but you could also check if value is empty, and/or add an onError callback to double.parse.
As displayed in the GIF below, the TextFormField i am using is entering the values backwards. I have set the TextDirection property to ltr as well but it did not change anything. Other TextFormFields dont seem to be having this issue. The entered text is being sent back to another screen using Navigator.pop and it is sent in the same backwards manned.
Weird TextFormField
Code for the textFormField causing the issue:
TextFormField(
// validator: (String value) {
// return value.isEmpty ? "task must have a name" : null;
// },
textDirection: TextDirection.ltr,
maxLength: 100,
controller: newcontroller, // Just an ordinary TextController
onChanged: (value) {
setState(() {
newcontroller.text = value;
});
},
decoration: InputDecoration(
errorText: _validate // Just a boolean value set to false by default
? 'Value Can\'t Be Empty' : null,
labelText: "name of task"
),
style: TextStyle(height: 1.2, fontSize: 20, color: Colors.black87)
)
You don't have to set text in newcontroller.text when onChanged is called.
text enter in your TextFormField is assigned by default to newcontroller.
You are getting this error because for this piece of code,
So, try to remove below code
setState(() {
newcontroller.text = value;
});
you can send whatever you want in Navigator.pop(context,whtever you want to pass)
TextFormField(
// validator: (String value) {
// return value.isEmpty ? "task must have a name" : null;
// },
textAlign: TextAlign.end,
maxLength: 100,
controller: newcontroller, // Just an ordinary TextController
onChanged: (value) {
print(value);
},
decoration: InputDecoration(
errorText:
_validate // Just a boolean value set to false by default
? 'Value Can\'t Be Empty'
: null,
labelText: "name of task"),
style:
TextStyle(height: 1.2, fontSize: 20, color: Colors.black87),
),
MaterialButton(
onPressed: () {
Navigator.pop(context, newcontroller.text);
},
child: Text("GO BACK!"),
),
replace onChanged with onEditingComplete.
onEditingComplete: (value) {
newController.text = value;
FocusScope.of(context).unfocus(); //use this to dismiss the screen keyboard
}
Just remove the onChanged function. There is no need for it.
onChanged: (val) {
if (val.isNotEmpty) {
setState(() {
// remember to not add your controller value in onchanged
_messageController.text = val;
isShowSendButton = true;
});
} else {
setState(() {
// remember to not add your controller value in onchanged
_messageController.text = val;
isShowSendButton = false;
});
}
Unfortunately, onEditingComplete doesn't always fire (i.e. on a focus change), so if you are editing a text variable you can still use onChanged as the most reliable way to update changes, but you should not use the onChanged value parameter, nor should you use setState (which results in the reversed text).
TextEditingController tec = TextEditingController(text: myText);
return TextField(
controller: tec,
onChanged: (value) {
myText = tec.text;
},
);