Changing focus from one text field to the next in Flutter - flutter

I have two textFormField widgets. Once the user has completed the first text field I would like to focus on the next textField. Is there a way to do this in Flutter? Currently, the done button just closes the keyboard. I was guessing the focusNode class might be the answer to this but not really sure how that works does anyone have any good examples of focusNode class? Thanks in advance.

Yes, FocusNode and the onFieldSubmitted from a TextFormField are probably the way to go.
FocusScope.of(context).requestFocus(focusNode);
Here is an example that may help:
FocusNode textSecondFocusNode = new FocusNode();
TextFormField textFirst = new TextFormField(
onFieldSubmitted: (String value) {
FocusScope.of(context).requestFocus(textSecondFocusNode);
},
);
TextFormField textSecond = new TextFormField(
focusNode: textSecondFocusNode,
);
// render textFirst and textSecond where you want
You may also want to trigger FocusScope.of() from a button rather than onFieldSubmitted, but hopefully the above example gives you enough context to construct an appropriate solution for your use case.

Screenshot:
No need to use FocusNode
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
TextField(
decoration: InputDecoration(hintText: 'First Name'),
textInputAction: TextInputAction.next,
onEditingComplete: () => FocusScope.of(context).nextFocus(),
),
TextField(
decoration: InputDecoration(hintText: 'Last Name'),
textInputAction: TextInputAction.done,
onSubmitted: (_) => FocusScope.of(context).unfocus(),
),
],
),
);
}

There's a similar method like in Android.
Add
textInputAction
parameter to the TextFormField Widget, then add the property as;
TextInputAction.next

This is how I did it:
var _focusNodes = List.generate(6, (index) => FocusNode()));
And in the TextFormField:
TextFormField(
focusNode: _focusNodes[i],
maxLines: 1,
textInputAction: TextInputAction.next,
onChanged: (text) {
if (i < _controllers.length) {
if (text.isEmpty)
_focusNodes[i - 1].requestFocus();
else
_focusNodes[i + 1].requestFocus();
}
},
),

Related

Flutter,I want my textfield with focus but hiding keyboard?

I need to scan data through a physical device using flutter textfield, but I don't have a solution yet I have gone through many and nothing works for me, please someone helps with this.
TextFormField(
focusNode: loginTextfieldFocus,
style: const TextStyle(fontSize: 15),
controller: loginTextController,
decoration: decoration(context, loginTextController),
onChanged: (val) {
if (val.contains('\n') || val.contains('\r')) {
Log.d("LoginTextfield Entered");
}
},
),
This is not the proper way to do , but somehow i managed to do it.
TextFormField(
autofocus: true,
enabled: isEditable,
showCursor: isEditable,
readOnly: true,
keyboardType: TextInputType.none,
focusNode: textfieldFocus,
controller: textfieldCtr
),
//Called this under widget build
#override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback((Duration _) {
FocusScope.of(context).requestFocus( textfieldFocus);
});
To receive scan data from device i used RawKeyboardListener. For scanning you can refer it here (flutter Enter event not found on zebra tc77?)
Yes, I have the same issue. I have solve by changing onChanged to validator because onChange is keep reloading your state thatswhy your keyboard is automatically dismiss every time.
validator: (value) {
if (value != null && value.trim().length < 10) {
return 'This field requires a minimum of 10 characters';
}
return null;
},
Try this
Widget build(BuildContext context) {
Future.delayed(const Duration(), () => SystemChannels.textInput.invokeMethod('TextInput.hide'));
return Scaffold(
body: TextField(
autofocus: true,
),
);
}

How to discard Backspace key press event in Textfield in Flutter (Web)?

I am creating a Flutter web app. The requirement is that under certain condition I have to block user from pressing backspace (or delete key) while typing some text in Textfield. I have tried RawKeyboardListener and TextEditingController. They help me listening the keyboard events, but I am unable to modify or discard keyboard events.
RawKeyboardListener(
focusNode: focusNode,
onKey: handleOnKeyEvent,
child: TextField(
textAlignVertical: TextAlignVertical.top,
textInputAction: TextInputAction.newline,
controller: textEditingController,
decoration: InputDecoration(
border: InputBorder.none, hintText: 'Start typing here'),
keyboardType: TextInputType.multiline,
minLines: null,
maxLines: null,
expands: true,
onChanged: (value) {
//print('text = $value');
handleUserInput(value);
},
),
),
How about using a TextController to check if text has been backspaced?
In this way the user can still type new characters but cannot delete them.
late String initialText;
late TextEditingController _textController;
void initState() {
initialText = "MyText";
_textController = TextEditingController(text: initialText);
super.initState();
}
and inside the build():
TextFormField(
controller: _textController,
onChanged: (input) {
if (_textController.text.length < initialText.length) {
_textController.text = initialText;
} else {
setState(() {
initialText = _textController.text;
});
}
},
)
For me, the TextField.onChange() and TextController callback were not worked.
After a bit of searching, I got this GitHub answer by Sonu-simon
https://github.com/flutter/flutter/issues/14809#issuecomment-907617733
Basically, It says to wrap TextField with the RawKeyboardListener widget. It has an onKey callback with RawKeyEvent passed in as a param.
I used it and it worked for me.
if (value.data.logicalKey.keyLabel == "Backspace") {
// stuff here...
}
In answer, Sonu-Simon explained to use the keyId property(to verify the backspace pressed), but it is different for each platform I think. So I have used the keyLabel property.

onEditingComplete is not called after unfocus

I have a TextField like this. The additional code is necessary to show that in different situations, I do various focus manipulation.
final node = FocusScope.of(context);
Function cleanInput = () => {controller.text = controller.text.trim()};
Function onEditingComplete;
Function onSubmitted
TextInputAction textInputAction;
if (!isLast) {
onEditingComplete = () => {
cleanInput(),
node.nextFocus(),
};
onSubmitted = (_) => {cleanInput()};
textInputAction = TextInputAction.next;
} else {
onEditingComplete = () => {
cleanInput(),
};
onSubmitted = (_) => {
cleanInput(),
node.unfocus(),
};
textInputAction = TextInputAction.done;
}
Widget textInput = TextField(
textInputAction: textInputAction,
controller: controller,
onEditingComplete: onEditingComplete,
onSubmitted: onSubmitted,
keyboardType: textInputType,
));
As you can see, I have functions I want to run onEditingComplete. However, this only gets called when I press the Next or Done buttons on my keyboard (or the Enter key in an emulator). If I change focus by tapping on a different field, this function does not get called.
I have tried using a Focus or FocusNode to help with this, but when I do so, the onEditingComplete function itself no longer works.
How can I get the desired effect here while everything plays nicely together?
Focus widget
Wrapping fields in a Focus widget might do the trick.
The Focus widget will capture focus loss events for children. With its onFocusChange argument you can call arbitrary functions.
Meanwhile, the onEditingComplete argument of TextField is unaffected and will still be called on the software keyboard "Next/Done" keypress.
This should handle field focus loss for both "Next/Done" keypress and user tapping on another field.
import 'package:flutter/material.dart';
class TextFieldFocusPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// ↓ Add this wrapper
Focus(
child: TextField(
autofocus: true,
decoration: InputDecoration(
labelText: 'Name'
),
textInputAction: TextInputAction.next,
// ↓ Handle focus change on Next / Done soft keyboard keys
onEditingComplete: () {
print('Name editing complete');
FocusScope.of(context).nextFocus();
},
),
canRequestFocus: false,
// ↓ Focus widget handler e.g. user taps elsewhere
onFocusChange: (hasFocus) {
hasFocus ? print('Name GAINED focus') : print('Name LOST focus');
},
),
TextField(
decoration: InputDecoration(
labelText: 'Password'
),
),
],
),
),
),
);
}
}
Please add a focus node to your textfield and add a listener to your focus node to trigger when it unfocuses
final node = FocusScope.of(context);
node.addListener(_handleFocusChange);
void _handleFocusChange() {
if (node.hasFocus != _focused) {
setState(() {
_focused = node.hasFocus;
});
}
}
Widget textInput = TextField(
//you missed this line of code
focusNode: node,
textInputAction: textInputAction,
controller: controller,
onEditingComplete: onEditingComplete,
onSubmitted: onSubmitted,
keyboardType: textInputType,
));
And also you can validete automatically by adding autoValidate to your code like below:
Widget textInput = TextField(
//add this line of code to auto validate
autoValidate: true,
textInputAction: textInputAction,
controller: controller,
onEditingComplete: onEditingComplete,
onSubmitted: onSubmitted,
keyboardType: textInputType,
));
FocusNode _node;
bool _focused = false;
#override
void initState() {
super.initState();
_node.addListener(_handleFocusChange);
}
void _handleFocusChange() {
if (_node.hasFocus != _focused) {
setState(() {
_focused = _node.hasFocus;
});
}
}
#override
void dispose() {
_node.removeListener(_handleFocusChange);
_node.dispose();
super.dispose();
}
TextFormField(
focusNode: _node)

How to check whether TextFormField is focused or not

I have a TextFormField with focusNode:
TextFormField(
key: Key('login-username-field-key'),
controller: loginTextController,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.text,
onFieldSubmitted: (term){
_changeFocusField(context, _loginFocus, _passwordFocus);
},
focusNode: _loginFocus,
decoration: InputDecoration(
labelText: AppLocalizations.of(context).loginFieldUsername
),
),
And then:
// Check that text field initially is not focused
final TextFormField textField = tester.widget(find.byKey(Key('login-username-field-key')));
expect(textField.focusNode.hasFocus, isFalse);
But from the docs I saw that TextFormField doesn't have 'focusNode' property (like TextField.focusNode.hasFocus).
So how to check that behavior?
PS I mean we can use FocusNode listeners, but I don't want to do that just for testing purposes. It should be really simply field.focusNode like for TextField.
You should add listener, where you can check state of your textFormField.
The method hasFocus return true or false.
#override
void initState() {
super.initState();
_loginFocus.addListener(_onFocusChange);
}
void _onFocusChange(){
debugPrint("Focus: "+_focus.hasFocus.toString());
}

How AutoFocus Textfield in flutter

I have a problem while developing my app - how can I auto-focus a textfield after completing input on the previous field, without having to click the textfield?
Use focusNode property and onEditingComplete event. Event trigger after user click on enter on first textfield, after that focusNode request focus for the second TextField.
For example:
FocusNode focusNode = FocusNode();
#override
Widget build(BuildContext context) => Scaffold(
body: Column(
children: <Widget>[
TextField(
autofocus: true,
onEditingComplete: (() => focusNode.requestFocus())
),
TextField(
focusNode: focusNode,
)
]
)
);
More info at: https://flutter.dev/docs/cookbook/forms/focus
TextField(
onEditingComplete: () => FocusScope.of(context).nextFocus(),
),
For the last textfield you would change the 'nextFocus()' to 'unfocus()'
return Focus(
child: TextField(
onChanged: (value) => context.read<SignupBloc>().add(SignupPasswordChanged(value)),
controller: controller,
keyboardType: TextInputType.visiblePassword,
obscureText: true,
key: const Key('signupForm_passwordInput_textField'),
decoration: InputDecoration(
labelText: 'Password *',
errorText: state.password.invalid
? 'Passwords must be at least 10 characters in length\nand a minimum of 1 '
'upper case and 1 lower case letter [A-Za-z]'
: null,
),
),
);
you can also try with
textInputAction: TextInputAction.next
in TextFormField