Flutter中如何监听焦点变化?

107
在Android中,我们可以调用setOnFocusChangeListener(),在onFocusChanged()方法中执行一些操作。但是Flutter没有像GestureDetector中的onTap()或RawKeyboardListener中的onKey()那样提供onFocus()接口。
我已经阅读了有关焦点的Flutter APIhttps://api.flutter.dev/flutter/widgets/FocusManager-class.html,但我无法找到实现我的请求的方法,有人能帮忙吗?

1
我同意 - 从文档中很难理解意图。最好提供一个例子。 - Ride Sun
Haider Abbas 发布了一个 回答,说:“您可以使用 MouseRegion() 事件,例如 https://api.flutter.dev/flutter/widgets/MouseRegion-class.html 的 onEnter、onExit”。 - Scratte
4个回答

190

此外,您还可以使用Focus小部件。

Focus(
  child: TextFormField(...),
  onFocusChange: (hasFocus) {
    if(hasFocus) {
      // do stuff
    }
  },
)

13
比起设置和维护一个FocusNode实例,这肯定更加简洁。而且此帖子中有一个适当的例子。看起来现在这可能是被接受的答案了。不错,谢谢。 - Gene Bo
7
如果你点击返回,这个功能不会触发。 - TimSim
2
这样做肯定更加简洁,但取决于使用情况。如果我们有多个输入字段,则为每个维护一个焦点节点会更好。 - Hassan Hammad
如果你正在使用块和可重用的小部件,这几乎是必须的! - nyphur
1
@chemturion 它会触发,但如果节点有焦点,则hasFocus将评估为true,如果没有焦点则评估为false。名称是恰当的。布尔值通常以命名约定has-、is-、did-等命名。 - keysmusician
显示剩余2条评论

111

使用 FocusNode

这里是完全正确的答案:addListener 应该在 initState() 中,而不是 build() 中,因为那样会导致每次小部件构建时添加一个监听器,你很可能不希望出现这种情况。

import 'package:flutter/material.dart';

class SomeWidget extends StatefulWidget {
  @override
  _SomeWidgetState createState() => _SomeWidgetState();
}

class _SomeWidgetState extends State<SomeWidget> {
  final _focusNode = FocusNode();
    
  @override
  void initState() {
    super.initState();
    _focusNode.addListener(() {
      print("Has focus: ${_focusNode.hasFocus}");
    });
  }
    
  @override
  Widget build(BuildContext context) {
    return TextField(focusNode: _focusNode);
  }
    
  @override
  void dispose() {
    _focusNode.dispose();
    super.dispose();
  }
}

使用 flutter_hooks

如果您偶然使用的是 flutter_hooks 包,则有专门的useFocusNode钩子。

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class SomeWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final focusNode = useFocusNode();
    useEffect(() {
      focusNode.addListener(() {
        print("Has focus: ${focusNode.hasFocus}");
      });
      return; // You need this return if you have missing_return lint
    }, [focusNode]);

    return TextField(focusNode: focusNode);
  }
}

谢谢@Albert221,我一直在解决接收多个调用的问题,现在我已经解决了。 - Luan Martins Gepfrie
11
注意:节点焦点监听器会被调用两次:当它获得焦点和失去焦点时。在回调函数中,node.hasFocus 分别为 truefalse - Kirill Karmazin
@Albert221 我们在dispose方法中是否也需要调用remove listener? - raphire
2
@raphire 不需要。在 ChangeNotifierFocusNode 是其一种)中通知监听器在其处理后是不合法的。 - Albert221
那么...如果第二个最终的focusNode不应该在那里,为什么它会出现在build方法中呢?或者说,如果它是一个HookWidget,它就应该在那里吗? - Karolina Hagegård
2
@KarolinaHagegård useFocusNode 是一个钩子,它返回 FocusNode,但也管理其生命周期。就像第一个代码片段中的 initStatedispose 一样,但是隐藏在钩子内部,如下所示:https://github.com/rrousselGit/flutter_hooks/blob/master/packages/flutter_hooks/lib/src/focus.dart#L50-L79 - Albert221

48

我猜你正在寻找 FocusNode 类。使用 addListener 方法添加一个监听函数来监听焦点变化。

示例
声明并定义FocusNode

 var focusNode = FocusNode();
  @override
  void initState() {
    focusNode.addListener(() {
      print(focusNode.hasFocus);
    });
    super.initState();
  }

在文本字段中使用焦点节点

TextField(
            focusNode: focusNode,
          ),

输出

当文本框获得焦点时,你将得到 true,否则为 false。


18
可以请您提供一个例子吗? - Stephane
2
一个例子,请。 - Sundeep Pidugu
不包括示例,答案是无用的。 - Kamlesh

-3
class FocusChange {
     static void fieldFocusChange(BuildContext context, FocusNode 
     currentFocusNode,
     FocusNode nextFocusNode) {
     currentFocusNode.unfocus();
     FocusScope.of(context).requestFocus(nextFocusNode);
    }
}

2
请记住,Stack Overflow 不仅是解决当前问题的地方,还可以帮助未来的读者找到类似问题的解决方案,这需要理解底层代码。对于我们社区中的初学者而言,这尤为重要,因为他们可能不熟悉语法。鉴于此,您能否编辑您的答案,包括解释您正在做什么以及为何认为这是最佳方法? - Jeremy Caney

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