如何为运行时创建的 TextField 调用 requestFocus?

How to call requestFocus for a TextField created at runtime?

我试图在运行时创建的 TextField 上调用 requestFocus 方法,但是函数 callFocus() 仅在我将延迟设置为 2 秒时才起作用,这可能是因为 TextField 是异步创建的。问题是,TextField准备好后,如何调用requestFocus?

appBar: AppBar(
  title: appBarTitle,
  centerTitle: true,
  actions: <Widget>[
    Center(
      child: IconButton(
        icon: actionIcon,
        onPressed: () {
          setState(() {
            if (this.actionIcon.icon == Icons.search) {
              this.actionIcon = Icon(Icons.navigate_next);
              this.appBarTitle = TextField(
                controller: editingController,
                focusNode: _searchInsideFocus,
                onChanged: (findNext) {
                  webView.findAllAsync(find: findNext);
                },
                style: TextStyle(
                  color: Colors.white,
                ),
                decoration: InputDecoration(
                    border: UnderlineInputBorder(
                        borderSide: BorderSide.none),
                    prefixIcon: Icon(Icons.search, color: Colors.white),
                    hintText: "Search...",
                    hintStyle: TextStyle(color: Colors.white)),
              ).;
            } else {
              webView.findNext(forward: true);
            }
          });
          callFocus();
        },
      ),
    ),
  )
Future<Null> callFocus() async {
  await new Future.delayed(const Duration(seconds : 2)); //only works if delayed
  FocusScope.of(context).requestFocus(_searchInsideFocus);
}

问题在于,如果以这种方式将文本字段动态添加到树中,则没有来自父小部件的回调让您知道文本字段何时呈现。相反,只需将焦点请求移动到一个新的包装器小部件中,该小部件确实知道它何时首次呈现,因此可以在呈现发生后请求焦点,如下所示:

import 'package:flutter/material.dart';

class TextWrapper extends StatefulWidget {
  const TextWrapper({
    this.controller,
    this.focusNode,
    this.onChanged,
  });

  final TextEditingController controller;
  final FocusNode focusNode;
  final ValueChanged<String> onChanged;

  @override
  _TextWrapperState createState() => _TextWrapperState();
}

class _TextWrapperState extends State<TextWrapper> {
  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: widget.controller,
      focusNode: widget.focusNode,
      onChanged: widget.onChanged,
      style: TextStyle(
        color: Colors.white,
      ),
      decoration: InputDecoration(
        border: const UnderlineInputBorder(
          borderSide: BorderSide.none,
        ),
        prefixIcon: Icon(Icons.search, color: Colors.white),
        hintText: 'Search...',
        hintStyle: TextStyle(color: Colors.white),
      ),
    );
  }

  void _onRendered(Duration _) {
    FocusScope.of(context).requestFocus(widget.focusNode);
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback(_onRendered);
  }
}

然后在您的 Scaffold() 中执行以下操作:

appBar: AppBar(
  title: appBarTitle,
  centerTitle: true,
  actions: <Widget>[
    Center(
      child: IconButton(
        icon: actionIcon,
        onPressed: () {
          setState(() {
            if (this.actionIcon.icon == Icons.search) {
              this.actionIcon = Icon(Icons.navigate_next);
              this.appBarTitle = TextWrapper(
                controller: editingController,
                focusNode: _searchInsideFocus,
                onChanged: (findNext) {
                  webView.findAllAsync(find: findNext);
                },
              );
            } else {
              webView.findNext(forward: true);
            }
          });
        },
      ),
    ),
  ],
),

希望对您有所帮助...

我相信焦点不会立即转移,因为在状态突变之后,flutter 引擎会尝试在更改状态小部件(即 IconButton)之前将焦点恢复到活动状态。如果在新状态下具有焦点的小部件消失了,则下一个具有 autofocus 属性 true 的小部件将处于活动状态。

这个行为值得使用。只需将文本字段的 autofocus 属性 设置为 true 并在单击后删除按钮:

import 'package:flutter/material.dart';

class LabPage extends StatefulWidget {
  @override
  _LabPageState createState() => _LabPageState();
}

class _LabPageState extends State<LabPage> {
  bool inSearch = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title: inSearch
              ? TextField(
                  autofocus: true,
                  onChanged: (findNext) {
                    // webView.findAllAsync(find: findNext);
                  },
                  style: TextStyle(
                    color: Colors.white,
                  ),
                  decoration: InputDecoration(
                      border: UnderlineInputBorder(borderSide: BorderSide.none),
                      prefixIcon: Icon(Icons.search, color: Colors.white),
                      hintText: "Search...",
                      hintStyle: TextStyle(color: Colors.white)),
                )
              : Text('My App Title'),
          centerTitle: true,
          actions: <Widget>[
            inSearch
                ? Container()
                : Center(
                    child: IconButton(
                      icon: Icon(Icons.search),
                      onPressed: () {
                        setState(() {
                          inSearch = true;
                        });
                      },
                    ),
                  ),
          ]),
      body: Container(),
    );
  }
}