如何正确聚焦根据焦点节点的 hasFocus 值有条件地创建的文本字段?

How can I correctly focus a Textfield that is conditionally created based on the focus node's hasFocus value?

使用我当前的代码,TextField 成为焦点,但未触发光标和键盘(需要第二次点击)。我认为这是因为最初聚焦节点时 TextField 不存在,但我正在努力寻找解决方案。

下面是基于 Cookbook 食谱的问题的简单重现:

class MyCustomForm extends StatefulWidget {
  @override
  _MyCustomFormState createState() => _MyCustomFormState();
}

class _MyCustomFormState extends State<MyCustomForm> {
  FocusNode myFocusNode;
  bool _editingField2 = false;

  @override
  void initState() {
    super.initState();
    myFocusNode = FocusNode();
    myFocusNode.addListener(_focusListener);
  }

  @override
  void dispose() {
    myFocusNode.dispose();
    super.dispose();
  }

  // Set _editingField2 to true when focusNode has focus.
  _focusListener() {
    if (myFocusNode.hasFocus) {
      setState(() {
        _editingField2 = true;
      });
    } else {
      setState(() {
        _editingField2 = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Text Field Focus'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            // The first text field is focused on as soon as the app starts.
            TextField(
              autofocus: true,
            ),
            // The second text field is created when _editingField2 (after FAB press).
            _editingField2
                ? TextField(
                    focusNode: myFocusNode,
                  )
                : Text('ayy'),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        // Give focus node focus on FAB press.
        onPressed: () => FocusScope.of(context).requestFocus(myFocusNode),
        tooltip: 'Focus Second Text Field',
        child: Icon(Icons.edit),
      ),
    );
  }
}

这是我的代码,对重要的部分进行了注释。

class TaskListItem extends StatefulWidget {
  final Task task;

  TaskListItem({@required this.task});

  @override
  State createState() => _TaskListItemState();
}

class _TaskListItemState extends State<TaskListItem> {
  bool _isEditing;
  FocusNode _focusNode;
  final TextEditingController _textEditingController = TextEditingController();

  @override
  initState() {
    super.initState();
    _isEditing = false;
    _textEditingController.text = widget.task.text;
    _textEditingController.addListener(_handleTextFieldUpdate);
    _focusNode = FocusNode(debugLabel: 'TaskListItem');
    _focusNode.addListener(_handleFocusChange);
  }

  @override
  void dispose() {
    _focusNode.removeListener(_handleFocusChange);
    _focusNode.dispose();
    _textEditingController.dispose();
    super.dispose();
  }

  _handleTextFieldUpdate() {
    Provider.of<TaskListModel>(context, listen: false)
        .updateTaskText(widget.task, _textEditingController.text);
  }

  // Update state to determine if Text or TextField widget is created in build().
  _handleFocusChange() {
    if (_focusNode.hasFocus) {
      setState(() {
        _isEditing = true;
      });
    } else {
      setState(() {
        _isEditing = false;
      });
    }
  }

  Widget _buildTitle() {
    return Row(
      children: <Widget>[
        Expanded(
          // Create either TextField or Text based on _isEditing value.
          child: _isEditing && !widget.task.isComplete
              ? TextField(
                  focusNode: _focusNode,
                  controller: _textEditingController,
                )
              : Text(
                  widget.task.text,
                  style: widget.task.isComplete
                      ? TextStyle(decoration: TextDecoration.lineThrough)
                      : null,
                ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: Checkbox(
        value: widget.task.isComplete,
        //Dismiss focus when box is checked
        onChanged: (bool checked) {
          _focusNode.unfocus();
          Provider.of<TaskListModel>(context, listen: false)
              .toggleComplete(widget.task);
        },
      ),
      title: _buildTitle(),
      trailing: IconButton(
        icon: Icon(Icons.delete),
        onPressed: () => Provider.of<TaskListModel>(context, listen: false)
            .deleteTask(widget.task),
      ),
      onTap: () {
        // I'm requesting focus here, but the Textfield doesn't exist yet?
        FocusScope.of(context).requestFocus(_focusNode);
        print('tapped');
      },
    );
  }
}

您需要做的是在构建内部更改焦点,您正试图在屏幕完成重建该小部件之前更改焦点。请尝试使用您自己的代码。

我不确定您是否需要真正聆听焦点变化,或者您是否只想在启用小部件后完成焦点变化,如果您确实想要聆听焦点变化,请告诉我。

class MyCustomForm extends StatefulWidget {
  @override
  _MyCustomFormState createState() => _MyCustomFormState();
}

class _MyCustomFormState extends State<MyCustomForm> {
  FocusNode myFocusNode = FocusNode();
  bool _editingField2 = false;

  @override
  void dispose() {
    myFocusNode?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {

   //here you do the focus request
   if (_editingField2) {
      FocusScope.of(context).requestFocus(myFocusNode);
   }

    return Scaffold(
      appBar: AppBar(
        title: Text('Text Field Focus'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            // The first text field is focused on as soon as the app starts.
            TextField(
              autofocus: true,
            ),
            // The second text field is created when _editingField2 (after FAB press).
            _editingField2
                ? TextField(
                    focusNode: myFocusNode,
                  )
                : Text('ayy'),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        // Give focus node focus on FAB press.
        onPressed: () {
          setState(() {
            _editingField2 = true;
          });
        },
        tooltip: 'Focus Second Text Field',
        child: Icon(Icons.edit),
      ),
    );
  }
}