0

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
Jarvis
  • 312
  • 3
  • 12

1 Answers1

0

You do have some compilation errors first that need to be addressed:

  • Remove the lonely setState(() { with no closing brace
  • Remove the extra dot from ..requestFocus();

Afterwards, test again and see if things start working.

Anis R.
  • 6,656
  • 2
  • 15
  • 37
  • 1. The closing brace is at the bottom of the `_addChild` function. 2. The extra dot is cascade notation. https://stackoverflow.com/questions/49447736/list-use-of-double-dot-in-dart The code compiles fine in it's current state. – Jarvis Aug 25 '20 at 19:58