I am using flutter to make a simple application.
In the below code, when TextBox changed event is fired then I call the method named updateTitle().
But I have to call the same method updateTitle(), when key is up, as we use in javascript and other languages too.
TextField(
controller: titleController,
style: textStyle,
onChanged: (value) => updateTitle(),
decoration: InputDecoration(
labelText: "Title",
labelStyle: textStyle,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
)),
),
For your use case, the onChangedworks just like onkyeup would work. Everytime that the user tap a new character in the textfield, it is fired.
In order to enable/disable a button you should listen to this event, do the test to see if the field isn't empty, modify a variable that will handle the button state, and call setState().
This is just a sample code. Not tested, but should work as is.
class _SoAnswerState extends State<SoAnswer> {
bool _buttonActive = false;
#override
Widget build(BuildContext context) {
...
TextField(
controller: titleController,
style: textStyle,
onChanged: (value) => updateButtonState(value), // onChanged return the value of the field
decoration: InputDecoration(
labelText: "Title",
labelStyle: textStyle,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
)
),
),
...
}
void updateButtonState(String text){
// if text field has a value and button is inactive
if(text != null && text.length > 0 && !_buttonActive){
setState(() {
_buttonActive = true;
}
}else if((text == null || text.length == 0) && _buttonActive){
setState(() {
_buttonActive = false;
}
}
}
Edit: add more information about the events
In JavaScript, the onkeyup event handler fires when the user releases a key that was previously pressed. When the user press and release a key inside a text field, the text field value changes. The onChanged listener in Flutter fires when the text field value changes. When working with a typing interface, where the user uses a tradicional keyboard, it is not a good idea to listen to all the changes of a text field, because the user can press and hold a key, leading the application to repeat the onchanged event too many times, once for every character repetition. That's not the case with a mobile interface, where the user (usually) can't press and hold a key.
Related
I have created a contact form with a few textformfields. When I'm using the keyboard I want to also use the the arrows at the top of the keyboard to move to next textformfield. I have already implemented TextInputAction.next so that when clicking on "return" you go to next textformfield, but I also want to include the arrows to do the same to go up/down.
The image below shows the arrows I want to include action on.
This is my current textformfield code:
Widget _createTextField(controller, keyboardType, int maxLength, int maxLines, String hintText, double screenWidth) {
return Expanded(
child: TextFormField(
scrollPadding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
textCapitalization: TextCapitalization.sentences,
textInputAction: TextInputAction.next,
style: TextStyle(fontSize: (screenWidth > maxMobileScreenWidth) ? 16 : 12),
controller: controller,
keyboardType: keyboardType,
maxLength: maxLength,
maxLines: maxLines,
validator: (value) {
if (value!.isEmpty) {
return '* Required field';
}
return null;
},
decoration: InputDecoration(
counterText: "",
filled: true,
hintText: hintText,
hintStyle: const TextStyle(color: Colors.black45),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10.0)),
fillColor: const Color(0xff9ccb40),
),
),
);
}
You must use the Form() widget, on top of all your TextFromField.
you can use Form(child:Column(children:[])
suggest you to read this article
I'm making a flutter application where user can type a message and hit send button IN THE KEYBOARD to send the message. The problem is when I press the send button the message gets send but the keyboard gets automatically dismissed. How can I prevent that from happening?
Thanks in advance.
TextField(
autofocus: true,
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: new InputDecoration.collapsed(
hintText: "Let's talk",
border: UnderlineInputBorder(
borderRadius: BorderRadius.circular(1),
),
),
textInputAction: TextInputAction.send,
onSubmitted: null,
)
TextField widget has a parameter just for this purpose!
While onSubmit callback can be used to handle business logic when the user presses done, there is also a property called onEditingComplete, specially made to handle focus-related logic. So you should use both, for better code readability.
According to the docs:
The default implementation of onEditingComplete executes 2 different
behaviors based on the situation:
When a completion action is pressed, such as "done", "go", "send", or
"search", the user's content is submitted to the controller and then
focus is given up.
When a non-completion action is pressed, such as "next" or "previous",
the user's content is submitted to the controller, but focus is not
given up because developers may want to immediately move focus to
another input widget within onSubmitted.
Therefore, if you don't like this behaviour, for example, "send" is considered a "completion action" here, thus in an Instant Messaging (chatting) app, each time user sends a short message, the keyboard will be collapsed. But if we override the onEditingComplete callback to an empty function, it will stop the default behavior and not hide the keyboard.
Sample code:
TextField(
controller: _controller,
onSubmitted: (value) {
sendMessage(text);
_controller.clear();
},
onEditingComplete: () {}, // this prevents keyboard from closing
textInputAction: TextInputAction.send,
)
Demo:
For complete explanation and comparison between onSubmitted and onEditingComplete callbacks, check out my other answer here.
This worked for me:-
First Create a FocusNode and assign it to your textfield, do the following :-
The FocusNode is a long lived component so initialize it in the initState method:-
FocusNode inputFieldNode;
#override
void initState() {
super.initState();
inputFieldNode = FocusNode();
}
Do not forget to cleanup the FocusNode in dispose method after the Form is disposed:-
#override
void dispose() {
inputFieldNode.dispose();
super.dispose();
}
Assign the FocusNode to the textfield and write the following code in onSubmitted::-
TextField(
focusNode: inputFieldNode,
onSubmitted: (String) => FocusScope.of(context).requestFocus(inputFieldNode),
)
Now the textfield will not lose focus even after pressing the submit button.
The cleanest approach would be to use onEditingComplete() with TextEditingController instead of onSubmitted(text). Refer below example.
final _controller = TextEditingController();
TextField(
controller: _controller,
padding: EdgeInsets.all(8),
textInputAction: TextInputAction.send,
placeholder: 'Type your message',
onEditingComplete: (){
if (_controller.text.isEmpty) return;
sendMessage(_controller.text);
},
),
// Focus node
FocusNode _myTextFieldFocusNode = FocusNode();
Widget build(BuildContext context) {
return SafeArea(
body: Focus(
onFocusChange: (hasFocus) => !hasFocus ? _myTextFieldFocusNode.requestFocus() : null
Container(
child: // child goes here
)
),
);
}
If the user taps to something like it can "dismiss the keyboard" Focus() widget will detect
changes and this is where you might want to put your _myTextFieldFocusNode.requestFocus()
keyboard won't have a chance to dismiss and reopen again
SystemChannels.textInput.invokeMethod('TextInput.show');
on onSubmit method
As soon as a TextField gets focused, an object which stores if it's enabled changes so that the TextField gets disabled immediately. This also happens when another TextField above gets focused.
The TextField is placed inside a StatefulWidget and a Category object contains another object called Goal which contains a bool variable if it's enabled or disabled. This variable is also used to enable or disable the TextField.
TextField(
controller: _goalAmountController,
enabled: widget.category.goal.enabled,
decoration: InputDecoration(
labelText: "Goal Amount",
border: OutlineInputBorder(),
),
onChanged: (value) {
try {
widget.category.goal.amount = double.parse(value);
} on Exception {
//TODO display error message
print("Invalid Goal-Amount");
}
},
),
There's also a switch below the TextField to enable or disable the Goal by setting it's enabled variable.
SwitchListTile(
value: widget.category.goal.enabled,
title: Text("Enable Goal"),
onChanged: (value) {
setState(
() {
widget.category.goal.enabled = value;
},
);
},
),
I found out that it seems as if the click on a TextField would replace the Goal object with a new one which has false as the default value for enabled.
Try to use FocusNodes instead :
FocusNode textNode = FocusNode();
TextField(
focusNode: textNode,
controller: _goalAmountController,
enabled: widget.category.goal.enabled,
decoration: InputDecoration(
labelText: "Goal Amount",
border: OutlineInputBorder(),
),
later when you want to disable this textField when the user interact with another widget you can call:
textNode.unfocus() ;
I'm trying to set an initial value for the text field. But I Can't set the initial value in text form field. I'm getting this error 'initialValue == null || controller == null': is not true .
code:
Widget buildFirstName(BuildContext context) {
valueBuilder = valueBuild();
return TextFormField(
controller: firstNameController,
initialValue: valueBuilder,
decoration: InputDecoration(
hintText: "Enter Name",
fillColor: Colors.white,
hintStyle: TextStyle(
color: Color.fromRGBO(0, 0, 0, 1.0),
fontFamily: "SFProText-Regular"),
),
validator: validatingName,
);
}
You can't use both initialValue and controller at the same time. So, it's better to use controller as you can set default text in its constructor.
Here is an example.
// Create the controller.
final controller = TextEditingController(text: "Your initial value");
Widget build(BuildContext context) {
return TextFormField(
controller: controller, // Assign it here.
// ...
);
}
To get the value entered by the user, use:
controller.text
You cannot have controller and initialValue for TextFormField at the same time which we know from #CopsOnRoad thread.
Besides of passing default text to the constructor of controller, you can modify the value of the TextFormField by assigning value to the controller text
firstNameController.text = valueBuilder;
In Android and iOS it is possible to change the enter/return key of the keyboard to e.g. a "Go" button (and other options).
On top, we can see the regular "Return" button on both systems, which is the one you get by default with no modifications in both Android & iOS native and Flutter.
Below that, there is another setting, again on both systems, which you can simply adjust in your native application. It is the "Go" button in this case.
The input action for a TextField (or TextFormField) can be specified like this (here, the Go button):
TextField(
textInputAction: TextInputAction.go
...
)
List of all available input actions.
This is how you can use textInputAction:
TextField(
textInputAction: TextInputAction.search,
onSubmitted: (value) {
print("search");
},
decoration: InputDecoration(
border: InputBorder.none,
prefixIcon: Icon(Icons.search),
hintText: 'Search ',
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
),
);
This is currently not possible.
Although you can edit flutter sources to make it possible quite easily.
The following edits are :
flutter/lib/src/widgets/editable_text.dart
Change _openInputConnection line ~430 to
void _openInputConnection() {
if (!_hasInputConnection) {
final TextEditingValue localValue = _value;
_lastKnownRemoteTextEditingValue = localValue;
_textInputConnection = TextInput.attach(this,
new TextInputConfiguration(
inputType: widget.keyboardType,
obscureText: widget.obscureText,
autocorrect: widget.autocorrect,
inputAction: widget.keyboardType == TextInputType.multiline
? TextInputAction.newline
: TextInputAction.done
)
)..setEditingState(localValue);
}
_textInputConnection.show();
}
In the same file, also declare a new field on EditableText class (not the state one) ~line 280
final TextInputAction textInputAction;
And assign it in EditableText constructor above line 164
this.textInputAction,
flutter/lib/src/material/text_field.dart
Same story. Add a new field, but to TextField instead :
final TextInputAction textInputAction;
and add the following to it's constructor :
this.textInputAction,
Finally, pass that new field as parameter to EditableText line 479 :
textInputAction: widget.textInputAction,
Done.
You can now inside your app specify a custom TextInputAction. This won't break existing TextField. It just adds the ability to override the default behavior.
new TextField(
keyboardType: TextInputType.text,
textInputAction: TextInputAction.newline,
),
basically we are use two types of text input fields,
TextField and TextFormField,
so,
for TextField,
TextField(
textInputAction: TextInputAction.go
.......
)
for TextFormField,
TextFormField(
textInputAction: TextInputAction.go,
........
)
both have textInputAction property, we can use this
Here is how to add the action button and also listen to onTap
For TextField
TextField(
textInputAction: TextInputAction.go,
onSubmitted: (value) {
print("Go button is clicked");
},
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
),
);
For TextFormField
TextFormField(
textInputAction: TextInputAction.go,
onFieldSubmitted: (value) {
print("Go button is clicked");
},
decoration: const InputDecoration(
hintText: "Type your search here",
hintStyle: TextStyle(color: Colors.black26),
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(40.0)),
),
contentPadding:
EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0)),
)
One angle ... I have not explored all the "keyboardType" options within the TextField (optional parameter of TextInputType).
But there are some obvious different keyboards for 'emailAddress' and 'datetime' and 'phone' - one of those options may emit the keyboard that you are looking for ...
Anyone looking for UIReturnKeyDone is like this:
TextField(
textInputAction: TextInputAction.done
...
)