i have a form pages contain a widget that will return various widget form according to the api parameters, for example: the parameters is 'type': 'text_box so the widget will return TextField widget if the parameters is 'type': 'dropdown_box' it will return dropdown widget.
This is just an example for what widget i make, the real problem is in the TextField widget because everytime i input in the field then press any other form widget(not pressing the complete button on textfield) and select the value it will return the focus to the TextField before if i press complete button the focus is neutral again what i want is when i press another widget the focus from the TextField is clear.
code
TextFormField(
minLines: widget.minLines,
inputFormatters: [
if (widget.isPrice)
MoneyInputFormatter(
thousandSeparator: ThousandSeparator.Period,
mantissaLength: 0,
),
],
controller: widget.controllerName,
onTap: () {
widget.onTap();
},
enabled: widget.enabled,
obscureText: widget.isObsecure,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w400,
color: (widget.textColor != null)
? widget.textColor
: widget.enabled
? Colors.black
: Colors.black38,
),
keyboardType: widget.keyboardType,
maxLines: widget.maxLines,
decoration: InputDecoration(
isDense: true, //remove default padding
fillColor:
widget.inputColor ?? Constants.colorAppointmentTextInput,
filled: true, // activate bg color
// hintText: widget.hintText,
hintText: widget.placeholder ?? 'Masukan ' + widget.hintText,
hintStyle: TextStyle(
fontSize: 14.0,
color: (widget.textColor != null)
? widget.textColor
: widget.enabled
? Colors.black54
: Colors.black38,
),
prefix: widget.prefix,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(
color: Constants.colorAppointmentBorderInput,
width: 0.5)),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(
color: Constants.colorAppointmentBorderInput,
width: 0.5)),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(
color: Constants.colorAppointmentBorderInput,
width: 0.5)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
BorderSide(color: Constants.redTheme, width: 0.5),
),
suffixIcon: widget.suffixIcon),
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
this is what i mean the widget will return widget according to the api
#override
Widget build(BuildContext context) {
if (widget.questionType == 'text_box') {
return TextFild();
} else if (widget.questionType == 'dropdown') {
return Dropdown();
} else if (widget.questionType == 'radio') {
return radioType();
}
}
Add this Line :
FocusScope.of(context).requestFocus(FocusNode());
if keyboard on it will make it off , if it's off it will make it on .
You could use a focus node:
myFocusNode = FocusNode()
Add it to your TextFormField:
TextFormField(
focusNode: myFocusNode,
...
);
When an event that you want to unfocus the text field occurs, like tapping some other button etc. use:
myFocusNode.unfocus();
Make sure to dispose of the FocusNode in your dispose method:
#override
void dispose() {
myFocusNode.dispose();
//...
super.dispose();
}
You can use FocusNode.
Add it to TextformField.
TextFormField(
focusNode: focusNode,
...
);
Call focusNode.unfocus(); on onFieldSubmitted, onSaved. In your case on click action when you show your dropdown.
Related
I have a TextField with its textDirection set to rtl (Right-to-Left). When I select the TextField, I expect the cursor go to the end, as usual, but cursor goes to one position before the end.
TextField(
textDirection: TextDirection.rtl,
controller: widget.controller,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(8),
isDense: true,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2,
color: Theme.of(context).primaryColor,
),
borderRadius: BorderRadius.circular(8),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 1.7,
color: Colors.grey.withOpacity(0.3),
),
borderRadius: BorderRadius.circular(8),
),
),
),
);
How can I make the cursor appear at the end instead?
UPDATE: I realized that specifying controller in the TextField make the problem appear. but i need cotroller in this situation.
I think this because of each arabic character is encoded as 2 to 4 bytes.
Any way this code can do the trick
TextField(
onTap: (){
if(controller.selection == TextSelection.fromPosition(TextPosition(offset: controller.text.length -1))){
setState(() {
controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length));
});
}
},
controller: controller,
...
);
You can control cursor position by using TextSelection.
I didn't test Arabic environment, but try this.
offset value means position of cursor, so test 0 or (widget.controller.text.length)
TextField(
textDirection: TextDirection.rtl,
controller: widget.controller
..selection = TextSelection.fromPosition(TextPosition(offset: 0)),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(8),
isDense: true,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2,
color: Theme.of(context).primaryColor,
),
borderRadius: BorderRadius.circular(8),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 1.7,
color: Colors.grey.withOpacity(0.3),
),
borderRadius: BorderRadius.circular(8),
),
),
),
);
You can try this action to move the cursor to the end of the text. Then place this controller in the controller in TextField.:
late TextEditingController _textEditingController;
#override
void initState() {
_textEditingController = TextEditingController(text: widget.text);
_textEditingController.selection = TextSelection.fromPosition(
TextPosition(offset: _textEditingController.text.length));
super.initState();
}
You can fix it by add this function on your textfield
onTap: () {
if (searchController.text[searchController.text.length - 1] != ' ') {
searchController.text = (searchController.text + ' ');
}
if (searchController.selection ==TextSelection.fromPosition(
TextPosition(offset:
searchController.text.length - 1))) {
setState(() {});
}
}
How do I restrict my user from using space in textfield by automatically removing that space when the user finish typing?
For example, if the user type King of Light, it will apply as KingofLight after he/she steps away from the textfield.
TextFormField(
initialValue: nickname != null
? nickname
: current_user.nickname,
decoration: InputDecoration(
border: new OutlineInputBorder(
borderSide: new BorderSide(color: Colors.grey),
borderRadius: BorderRadius.circular(6),
),
focusedBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Colors.grey, width: 1.0),
borderRadius: BorderRadius.circular(6),
),
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Colors.grey, width: 1.0),
borderRadius: BorderRadius.circular(6),
),
hintText: 'Empty',
hintStyle: TextStyle(
color: Colors.grey[400],
fontSize: 20,
//fontWeight: FontWeight.bold,
),
),
style: TextStyle(
fontSize: 20,
// fontWeight: FontWeight.bold,
),
validator: (val) => val.length < 2
? 'Enter a nickname 2+char long'
: null,
onChanged: (val) {
val = val.replaceAll(' ', '');
setState(() => nickname = val);
},
),
please help me! thank you!
Text field which does not allow spaces, using RegExp. As below-
TextFormField(
inputFormatters: [
if (denySpaces)
FilteringTextInputFormatter.deny(
RegExp(r'\s')),
])
Above solution worked for me, to block space from keyboard.
One way you do this is like this using TextEditingController and can call formatNickname() as per your use case.
class _MyWidgetState extends State<MyWidget>{
FocusNode node = new FocusNode();
TextEditingController tc = TextEditingController();
#override
void initState(){
node.addListener((){
if(!node.hasFocus){
formatNickname();
}
});
super.initState();
}
void formatNickname(){
tc.text = tc.text.replaceAll(" ", "");
}
#override
Widget build(BuildContext context) {
return Column(
children: [
TextFormField(
focusNode: node,
controller: tc,
),
TextFormField(),
RaisedButton(
child: Text('Format'),
onPressed: (){
formatNickname();
},
),
],
);
}
}
I have a TextField widget to add players in a List. But I want the keyboard to stay focus when I add players, and on submit, my keyboard keeps loosing focus.. Any Idea ?
Here is my TextField widget:
TextField(
textCapitalization: TextCapitalization.words,
onChanged: (val) => playerName = val.trim(),
onSubmitted: (val) {
if (playerName != null && playerName != '') {
Provider.of<PlayerProvider>(context, listen: false).addPlayer(playerName);
HapticFeedback.lightImpact();
}
},
maxLength: 19,
autocorrect: false,
decoration: new InputDecoration(
counterText: "",
border: new OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: const BorderRadius.all(
const Radius.circular(30.0),
),
),
filled: true,
contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
hintStyle: GoogleFonts.rubik(color: Colors.grey[500], fontWeight: FontWeight.bold),
hintText: AppLocalizations.of(context).translate('player_selection_page_hint'),
fillColor: Colors.white),
)
By the way, the autofocus: true works but it kinda unFocus the keyboard and give the focus back instantly... So it's not nice to watch. So If you have another idea please.
I think you should try using FocusNode.
class MyCustomForm extends StatefulWidget {
#override
_MyCustomFormState createState() => _MyCustomFormState();
}
// Define a corresponding State class.
// This class holds data related to the form.
class _MyCustomFormState extends State<MyCustomForm> {
// Define the focus node. To manage the lifecycle, create the FocusNode in
// the initState method, and clean it up in the dispose method.
FocusNode myFocusNode;
#override
void initState() {
super.initState();
myFocusNode = FocusNode();
}
#override
void dispose() {
// Clean up the focus node when the Form is disposed.
myFocusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return TextField(
focusNode: myFocusNode
textCapitalization: TextCapitalization.words,
onChanged: (val) => playerName = val.trim(),
onSubmitted: (val) {
if (playerName != null && playerName != '') {
Provider.of<PlayerProvider>(context, listen: false).addPlayer(playerName);
HapticFeedback.lightImpact();
}
myFocusNode.requestFocus();
},
maxLength: 19,
autocorrect: false,
decoration: new InputDecoration(
counterText: "",
border: new OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: const BorderRadius.all(
const Radius.circular(30.0),
),
),
filled: true,
contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
hintStyle: GoogleFonts.rubik(color: Colors.grey[500], fontWeight: FontWeight.bold),
hintText: AppLocalizations.of(context).translate('player_selection_page_hint'),
fillColor: Colors.white),
);
}
}
Well, you can go with FocusNode.
But I have a workaround for this.
I usually set the
autovalidate:true
and in validator always return some text.
So whenever the user clicks on the textfield it never loses the focus.
Thus the flutter always keeps the focus on the text field.
But for this, you've to use TextFormField.
But the correct way is to go with FocusNode.
Here's the Code.
TextFormField (
autovalidate : true
validator: (value) {
return '';
},
textCapitalization: TextCapitalization.words,
onChanged: (val) => playerName = val.trim(),
onSaved: (val) {
if (playerName != null && playerName != '') {
Provider.of<PlayerProvider>(context, listen: false).addPlayer(playerName);
HapticFeedback.lightImpact();
}
},
maxLength: 19,
autocorrect: false,
decoration: new InputDecoration(
counterText: "",
border: new OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: const BorderRadius.all(
const Radius.circular(30.0),
),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: const BorderRadius.all(
const Radius.circular(30.0),
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: const BorderRadius.all(
const Radius.circular(30.0),
),
),
filled: true,
contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
hintStyle: GoogleFonts.rubik(color: Colors.grey[500], fontWeight: FontWeight.bold),
hintText: AppLocalizations.of(context).translate('player_selection_page_hint'),
fillColor: Colors.white),
)
and then you can use this to call the onSaved method
_formKey.currentState.save(); // And it will call the onSaved method
Here the _formKey
Form(
key: _formKey,
child: Widget(),
),
Wrap the whole widget tree where your textformfield is present.
I cant get the textfield labeltext to show. anyone can help? and how do I get the border to always showing and not disappear when I click on the text field.
#override
Widget build(BuildContext context) {
return TextFormField(
controller: _controller,
decoration: InputDecoration(
border: new OutlineInputBorder(
borderSide: new BorderSide(width: 1)
),
// hintText: _hintText,
labelText: "Label Text",
contentPadding: EdgeInsets.only(left: 15),
// suffixText: _nameLength.toString() + "/ " + _maxLength.toString(),
// suffixStyle: TextStyle(fontSize: 12),
// suffixStyle: TextStyle(color: Theme.of(context).textTheme.bodyText2.color),
// counterText: "",
),
maxLines: _maxLines,
textCapitalization: TextCapitalization.sentences,
cursorRadius: Radius.circular(10),
keyboardType: TextInputType.text,
autofocus: true,
maxLength: _showTextCount ? _maxLength : null,
style: TextStyle(color: AppSetting.textColor1),
validator: (val) {
// if (val.isEmpty) {
// return 'Please enter text.';
// }
// return null;
},
onChanged: (val) {
_onChanged(val);
setState(() {
_inputFieldText = val;
_nameLength = _inputFieldText.length;
});
},
onTap: () => AppSetting.hapticFeeback(),
);
}
}
You should add
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.redAccent,
width: 2.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: primaryColor,
width: 2.0,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.black54,
width: 2.0,
),
),
),
Just make sure the default value of labelColor of the input decoration is different from background (the default label color was white in my case). In order to show border when clicked set focusedBorder property.
I want to change the color of the text when the user enters something. But when only if the user enter something. Otherwise, it should remain the same that is grey. For this, I am creating a Focus Node inside the component and assigning it to the textformfield focusNode property. In initState I am adding a listener so whenever my textformfield has focus I can change the color of the hint text as well as the border color. But it is giving me an error and I am not able the change the color of the user enter text.
FocusNode _focusNode = FocusNode();
#override
void initState() {
_focusNode.addListener(() {
setState(() => color = _focusNode.hasFocus ? Colors.blue : Colors.grey);
});
super.initState();
}
#override
void dispose() {
_focusNode.dispose();
super.dispose();
}
And my component is like this below :
return Container(
height: MediaQuery.of(context).size.height * 0.06,
child: Material(
elevation: 5.0,
borderRadius: BorderRadius.all(
Radius.circular(30.0),
),
child: TextFormField(
controller: textController,
focusNode: _focusNode,
onTap: _requestFocus,
keyboardType: this.widget.type,
textAlignVertical: TextAlignVertical.bottom,
onChanged: (value) => {
setState(() {
_color = Colors.blue;
})
},
style: TextStyle(
color: color,
fontWeight: FontWeight.w400,
fontSize: 15.0,
),
obscureText: widget.obscureText,
decoration: InputDecoration(
filled: true,
fillColor: Color(0xFFFCFCFC),
hintText: this.widget.hintText,
isDense: true,
contentPadding: EdgeInsets.all(40.0),
hintStyle: TextStyle(
color: _focusNode.hasFocus ? Colors.blue : Colors.grey,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(50.0),
),
borderSide: const BorderSide(color: Colors.white, width: 0.0),
),
prefixIcon: Icon(
this.widget.icon,
color: Colors.grey,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(50.0)),
borderSide: BorderSide(
color: Colors.blue,
),
),
),
validator: (String value) {
return;
},
),
),
);
I am not able to determine what I am doing wrong.
Edit: I have move the initialization of the focus node in initSate and in onChanged property i am setting the color to grey within setState. but still giving error Failed assertion: line 742 pos 12: 'attached': is not true.
I solved the issue by doing these changes :
void _requestFocus() {
setState(() {
FocusScope.of(context).unfocus();
FocusScope.of(context).requestFocus(_focusNode);
});
}
removing onChanged call back and giving the color blue to the textfield.
Thats it.