如何在Flutter中检测TextField被选中?

95

我有一个 Flutter TextField,当选择该字段时,软键盘会覆盖它。 我需要在显示键盘时将该字段向上滚动并躲开。 这是一个相当普遍的问题,解决方案在此 StackOverflow 帖子中提供。

我认为我已经弄清楚了 ScrollController 部分,但是如何检测 TextField 何时被选择呢? 看起来没有任何事件方法(例如 onFocus()、onSelected()、onTap() 等)。

我尝试将 TextField 包装在 GestureDetector 中,但那也不起作用--显然事件从未被捕获。

new GestureDetector(
  child: new TextField(
    decoration: const InputDecoration(labelText: 'City'),
  ),
  onTap: () => print('Text Selected'),
),

这是一个非常基本的要求,我知道一定有一个简单的解决方案。

4个回答

151

我想你正在寻找 FocusNode

要监听焦点更改,您可以将侦听器添加到FocusNode中,并将focusNode指定为TextField

示例:

class TextFieldFocus extends StatefulWidget {
  @override
  _TextFieldFocusState createState() => _TextFieldFocusState();
}

class _TextFieldFocusState extends State<TextFieldFocus> {
  FocusNode _focus = FocusNode();

  TextEditingController _controller = TextEditingController();

  @override
  void initState() {
    super.initState();
    _focus.addListener(_onFocusChange);
  }

  @override
  void dispose() {
    super.dispose();
    _focus.removeListener(_onFocusChange);
    _focus.dispose();
  }

  void _onFocusChange() {
    debugPrint("Focus: ${_focus.hasFocus.toString()}");
  }
  
  @override
  Widget build(BuildContext context) {
    return new Container(
      color: Colors.white,
      child: new TextField(
        focusNode: _focus,
      ),
    );
  }
}

这个代码片段演示了如何确保聚焦的节点在用户界面上可见。


非常感谢您的回答。我还没有尝试使用您在gist中提供的更强大的解决方案,但是下面的简单解决方案似乎在我的测试用例中有效。 - user2785693
我对这个解决方案有问题,因为我正在文本字段上显示一个覆盖层。显示覆盖层非常顺利,但是一旦关闭覆盖层,文本字段就会再次自动聚焦,然后覆盖层再次弹出。 - Qiong Wu
在显示对话框之前,只需在相同的焦点节点上调用unfocus方法。仅在您回来时不想让文本字段聚焦时才这样做。 - Hemanth Raj
4
在有状态的小部件的dispose()方法中,还应清除焦点节点。请参见https://api.flutter.dev/flutter/widgets/FocusNode-class.html上的示例。 - stpch
1
不要忘记在dispose()中处理它。 - Hesam
在dispose()中,使用focus.removeListner和focus.dispose。 - Indy9000

68

要接收聚焦事件通知,可以使用工具类FocusScopeFocus来避免手动管理小部件的状态。

根据文档(https://api.flutter.dev/flutter/widgets/FocusNode-class.html):

请参阅Focus和FocusScope小部件,它们是管理自己的FocusNodes和FocusScopeNodes的实用小部件。如果它们不合适,也可以直接管理FocusNodes。

这里是一个简单的示例:

FocusScope(
  child: Focus(
    onFocusChange: (focus) => print("focus: $focus"),
    child: TextField(
      decoration: const InputDecoration(labelText: 'City'),
    )
  )
)

1
对我而言,这个答案比被采纳的答案更好,因为回调函数只有在子小部件“TextField”被点击输入文本时才会调用。使用被采纳答案中的“addListener”方法,则不是这种情况。一些父小部件也会在被点击时触发回调函数,我并没有完全理解为什么会这样。 - TGLEE
1
在安卓系统中,如果您在文本字段中按下系统返回按钮,则不会触发 [onFocusChange] 事件。 - Marwin Lebensky
但是当我们触发弹出窗口时,焦点作用域会触发对焦点的操作,所以我的日期选择器会在此基础上打开。 - Yogi Arif Widodo

23

最简单的解决方案是在TextField上添加onTap方法。

TextField(
  onTap: () {
    print('Editing stated $widget');
  },
)

7
TextField 还有其他获取焦点的方法,例如如果它是页面上的第一个 TextField,未来的 Flutter 版本将支持通过制表键在字段之间切换焦点。 - Günter Zöchbauer

3

如果你的文本字段需要因某种目的而被禁用,那么还有另一种方式。对于这种情况,你可以像下面这样使用InkWell来包装你的textField:

InkWell(
  onTap: () {
    print('clicked');
  },
  child: TextField(
    enabled: false,
  ),
);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接