'FocusNode - Why is requestFocus() not working?
The following is the relevant code. The debugPrint() in the onFieldSubmitted: is running, so the code is getting here, but the focus is not changing. Focus does not change.
_createItemHeadingWidget() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
controller: _controllerItemHed,
autofocus: true,
focusNode: _focusNodeItemHed,
textInputAction: TextInputAction.next,
onFieldSubmitted: (v) {
debugPrint("ItemHeading submitted");
FocusScope.of(context).requestFocus(_focusNodeItemDetail);
},
decoration: InputDecoration(
labelText: "Item Heading",
hintText: "Enter item heading",
border: OutlineInputBorder(borderRadius: BorderRadius.circular(20.0)),
),
),
);
}
Widget _createItemDetailWidget() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
controller: _controllerItemDetail,
maxLength: 75,
focusNode: _focusNodeItemDetail,
textInputAction: TextInputAction.next,
onFieldSubmitted: (v) {
debugPrint("ItemDetail submitted");
FocusScope.of(context).requestFocus(_focusNodeItemPrice);
},
decoration: InputDecoration(
labelText: "Item Detail",
hintText: "Enter item detail",
border: OutlineInputBorder(borderRadius: BorderRadius.circular(20.0)),
),
),
);
}
class DataEntryState extends State<DataEntry> {
final TextEditingController _controllerItemHed = TextEditingController();
final TextEditingController _controllerItemDetail = TextEditingController();
final TextEditingController _controllerItemPrice = TextEditingController();
final TextEditingController _controllerSeller = TextEditingController();
final TextEditingController _controllerContactNr = TextEditingController();
final TextEditingController _controllerLocation = TextEditingController();
final FocusNode _focusNodeItemHed = FocusNode();
final FocusNode _focusNodeItemDetail = FocusNode();
final FocusNode _focusNodeItemPrice = FocusNode();
final FocusNode _focusNodeSeller = FocusNode();
final FocusNode _focusNodeContactNr = FocusNode();
final FocusNode _focusNodeLocation = FocusNode();
Solution 1:[1]
After trying many things that did not work, the only solution that I could find is as follows:
_changeFocus(BuildContext context, FocusNode focusNodeCurrent,
FocusNode focusNodeNext) {
focusNodeCurrent.unfocus();
setState(() => _focusNodeCurrent = focusNodeNext);
}
debugPrint() showed that in _changeFocus(), unfocus() does work, and that requestFocus() did not work.
In the build :
if (_focusNodeCurrent != null)
FocusScope.of(context).requestFocus(_focusNodeCurrent);
Other relevant code (some of which is probably redundant) :
TextFormField _createItemHeadingWidget(BuildContext context) {
FocusScope.of(context).reparentIfNeeded(_focusNodeItemHed);
return TextFormField(
controller: _controllerItemHed,
maxLength: 50,
keyboardType: TextInputType.text,
autofocus: _dataRec == null,
focusNode: _focusNodeItemHed,
textInputAction: TextInputAction.next,
onFieldSubmitted: (v) {
_changeFocus(context, _focusNodeItemHed, _focusNodeItemDetail);
},
decoration: InputDecoration(
labelText: "Item Heading",
hintText: "Enter item heading",
border: OutlineInputBorder(borderRadius: BorderRadius.circular(20.0)),
),
);
}
Solution 2:[2]
Use
FocusScope.of(context).unfocus();
instead of
FocusScope.of(context).requestFocus(new FocusNode());
Solution 3:[3]
firstFieldFocusNode.nextFocus();
Did the trick for me
Solution 4:[4]
Doing setState() to solve this problem didn't seem to me like the optimum solution, so I kept testing. After a lot of further testing which mainly involved removing everything that I could from the app, I eventually replaced the creation of MaterialApp with creation of Scaffold. This fixed the problem. Now it appears to work as intended. All that is required is to do is:
_changeFocus(BuildContext context, FocusNode focusNodeNew) {
FocusScope.of(context).requestFocus(focusNodeNew);
}
I presume that the reason I was initially creating a MaterialApp is that I copied that from another app when I was initially starting to learn Flutter. It appears that it's not necessary to create a MaterialApp, but I don't know the reasons for creating a MaterialApp as opposed to creating just the Scaffold.
Whether it is a bug in flutter that causes this behavior, I don't know. It may be just a "feature" of using MaterialApp, or perhaps I was misusing MaterialApp.
Solution 5:[5]
For anyone else struggling with this I was just playing around and made a demo.
You can switch the focus to the left or right textfields or cancel the focus. The code below is a combination of @Brian Oh 's answers and some new code. Features:
- keeps track of all nodes including no focus and current focus
- changeFocus function only requires nextFocus parameter, doesn't use
FocusScope.of(context).requestFocus(_nextFocusNode)and doesn't have to set state
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: Home(),);
}
}
class Home extends StatefulWidget {
@override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
FocusNode currentFocusNode;
FocusNode leftFocusNode;
FocusNode rightFocusNode;
FocusNode noFocusNode;
@override
void initState() {
super.initState();
currentFocusNode = FocusNode();
leftFocusNode = FocusNode();
rightFocusNode = FocusNode();
noFocusNode = FocusNode();
}
@override
void dispose() {
//currentFocusNode.dispose();
//leftFocusNode.dispose();
//rightFocusNode.dispose();
//noFocusNode.dispose();
super.dispose();
}
changeFocus(FocusNode focusNodeNext) {
currentFocusNode.unfocus();
focusNodeNext.requestFocus();
currentFocusNode = focusNodeNext;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.fromLTRB(0, 40, 0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
children: <Widget>[
Container( width: 50, child: TextField(
focusNode: leftFocusNode,
),),
RaisedButton(
child: Text('^ Focus ^'),
onPressed: () => changeFocus(leftFocusNode),
),
],
),
Column(
children: <Widget>[
Container( width: 50, child: TextField(
focusNode: rightFocusNode,
),),
RaisedButton(
child: Text('^ Focus ^'),
onPressed: () => changeFocus(rightFocusNode),
),
],
),
RaisedButton(
child: Text(' Cancel Focus'),
onPressed: () => changeFocus(noFocusNode),
),
],
),
),
);
}
}
EDIT:
I used this code in another app and got an error
A FocusNode was used after being disposed.
according to this github link you should't call dispose. I've commented out the dispose calls made to focus nodes above and the error disappeared.
Solution 6:[6]
Using FocusScope.of(context).nextFocus() has worked for me
Custom Widget Code I Used
CupertinoTextFormFieldRow customTextField(
String pretext, String supportText,
TextEditingController controller, Function(String) validatorFun) {
return CupertinoTextFormFieldRow(
controller: controller,
prefix: Text(pretext),
placeholder: supportText,
validator: (val) => validatorFun(val!),
textInputAction: TextInputAction.next,
onEditingComplete: () {
FocusScope.of(context).nextFocus();
},
);
}```
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Brian Oh |
| Solution 2 | aboger |
| Solution 3 | woshitom |
| Solution 4 | Brian Oh |
| Solution 5 | |
| Solution 6 | Maddu Swaroop |

