I'm trying to make a list of items that a user can add to. To achieve that, I decided to use a ListView widget where each row is a TextFormField widget. I want the newly added item to get the focus as soon as it is painted.
I followed this recipe: https://api.flutter.dev/flutter/widgets/Focus-class.html#widgets.Focus.3
Where there is a comment explaining that calling .requestFocus()
before build should be fine.
But when I replace the ActionChip
widget with the TextFormField
widget, the new item does not get the focus.
Here is my code:
class SettingsWidget extends StatefulWidget {
SettingsWidget(this.addIngredientsListener);
final StreamController<String> addIngredientsListener;
@override
SettingsWidgetState createState() => SettingsWidgetState();
}
class SettingsWidgetState extends State<SettingsWidget> {
final List<Widget> children = new List<Widget>();
final List<FocusNode> childFocusNodes = new List<FocusNode>();
@override
void initState() {
super.initState();
widget.addIngredientsListener.stream.listen(_addChild);
_addChild("init");
_addChild("init");
}
@override
void dispose() {
super.dispose();
childFocusNodes.forEach((node) => node.dispose());
}
void _addChild(String caller) {
setState(() {
String text = 'CHILD ${children.length}';
// Calling requestFocus here creates a deferred request for focus, since the
// node is not yet part of the focus tree.
FocusNode newNode = FocusNode(debugLabel: 'Child ${children.length}')..requestFocus();
childFocusNodes
.add(newNode);
TextEditingController textController = TextEditingController(
text: text);
children.add(
TextFormField(
autofocus: false,
focusNode: newNode,
controller: textController
));
newNode.requestFocus();
});
}
@override
Widget build(BuildContext context) {
Widget widget = ListView.separated(
padding: const EdgeInsets.all(16.0),
itemCount: children.length,
itemBuilder: (context, i) {
return children[i];
},
separatorBuilder: (BuildContext context, int index) => const Divider());
return widget;
}
}
I have successfully manually requested focus on the already existing TextFormFields in the list. Because of that, I assume there must be something stopping the new one from receiving focus.
I have a workaround where the manually added TextFormFields have autofocus: true
and the ones made by initState()
do not. It just bugs me that manually requesting focus on the new item does not work.
I have also seen This question on S/O where the thread waits a few ms to call requestFocus()
. It also works for me.
Is there really no better alternative than to thread sleep the focus call?
Version:
[✓] Flutter (Channel stable, v1.12.13+hotfix.9, on Linux, locale en_GB.UTF-8)
• Flutter version 1.12.13+hotfix.9 at /opt/flutter
• Framework revision f139b11009 (5 months ago), 2020-03-30 13:57:30 -0700
• Engine revision af51afceb8
• Dart version 2.7.2