How to shift focus to the next TextField in Flutter?
I am new to Flutter.
I am building a form with multiple text inputs using following widgets: Form, TextFormField. The keyboard that appears doesn't show "next" (which should shift the focus to next field) field action instead it is "done" action (which hides the keyborad).
I looked for any hints in official docs, found nothing directly that can be done. I although landed on FocusNode(cookbook, api doc). It provides with mechanism to shift focus by some button or any other action on app, but I want it to be in keyboard.
Solution 1:
Screenshot:
Just use:
textInputAction: TextInputAction.next
: To move the cursor to the next field.
textInputAction: TextInputAction.done
: To close the keyboard.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(hintText: 'TextField A'),
textInputAction: TextInputAction.next, // Moves focus to next.
),
TextField(
decoration: InputDecoration(hintText: 'TextField B'),
textInputAction: TextInputAction.next, // Moves focus to next.
),
TextField(
decoration: InputDecoration(hintText: 'TextField C'),
textInputAction: TextInputAction.done, // Hides the keyboard.
),
],
),
);
}
Solution 2:
Found a way to achieve it.
Displaying Next Icon instead of Done - setting
textInputAction
parameter toTextInputAction.next
-
Using
onFieldSubmitted
callback to request focus node of next field.class FormWidget extends StatelessWidget{ final focus = FocusNode(); @override Widget build(BuildContext context) { return Form( child: SingleChildScrollView( padding: EdgeInsets.symmetric(horizontal: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ TextFormField( textInputAction: TextInputAction.next, autofocus: true, decoration: InputDecoration(labelText: "Input 1"), onFieldSubmitted: (v){ FocusScope.of(context).requestFocus(focus); }, ), TextFormField( focusNode: focus, decoration: InputDecoration(labelText: "Input 2"), ), ], ), ), ); } }
Edit: As stated in the documentation (flutter.io/docs/cookbook/forms/focus), - we also need to manage FocusNode lifecycle. So, init FocusNode in the init() method and dispose in dispose() of the parent Widget. - @AntonDerevyanko
Update: The same can be achieved without FocusNode
and FocusScopeNode
, by simply calling FocusScope.of(context).nextFocus()
, take a look at CopsOnRoad solution on how to do that. For more info check doc.
Solution 3:
This is additional steps to CopsOnRoad answer since it doesn't work in more complex UI when there are focusable widgets in between text fields, for example:
- when password field has a clickable toggle icon
- when there is a button (or some other focusable widget) between fields...
the solution here is to keep calling 'nextFocus()' until 'EditableText' is found
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(hintText: "TextField A"),
textInputAction: textInputAction1,
onSubmitted: (_) => context.nextEditableTextFocus(), // move focus to next
),
TextField(
decoration: InputDecoration(hintText: "TextField B"),
textInputAction: textInputAction2,
onSubmitted: (_) => context.nextEditableTextFocus(), // move focus to next
),
MaterialButton(
onPressed: () {},
color: Colors.amber,
),
TextField(
decoration: InputDecoration(hintText: "TextField C"),
textInputAction: textInputAction3,
onSubmitted: (_) => FocusScope.of(context).unfocus(), // submit and hide keyboard
),
],
),
);
}
Where the extension method is:
extension Utility on BuildContext {
void nextEditableTextFocus() {
do {
FocusScope.of(this).nextFocus();
} while (FocusScope.of(this).focusedChild?.context?.widget is! EditableText);
}
}