Flutter RawKeyboardListener页面刷新后按键事件多次触发如何解决
问题原因
- 核心问题1:你在
build方法内直接新建了FocusNode实例,每次页面刷新触发build执行时,都会生成新的FocusNode,旧实例没有被主动销毁,绑定的onKey回调会一直留存,每次重绘就多一个监听器。 - 核心问题2:计数器
counter用了static修饰,属于类级别的全局变量,State销毁重建时变量值不会重置,叠加多个监听器的回调触发,就会出现每次按键数值翻倍增长的现象。 - 额外触发条件:
autofocus: true配置会让每次新建的FocusNode自动申请焦点,所有留存的监听器都能接收到按键事件。
最佳解决方案
- 将
FocusNode改为State的成员变量,在initState中仅初始化一次,在dispose生命周期中主动销毁释放资源 - 移除
counter的static修饰符,改为实例成员变量,保证State销毁重建时数值会重置 - 在
onKey回调中增加事件类型判断,避免按键按下、抬起各触发一次回调的问题
修正后的代码示例:
class MyHomePageState extends State<MyHomePage> { // 改为实例变量,移除static修饰 int counter = 0; // 定义全局唯一的FocusNode成员变量 late FocusNode _keyboardFocusNode; @override void initState() { super.initState(); // 初始化仅执行一次 _keyboardFocusNode = FocusNode(); } @override void dispose() { // 主动销毁FocusNode,释放绑定的回调资源 _keyboardFocusNode.dispose(); super.dispose(); } void _incrementCounter() { setState(() { counter++; }); } void _decrementCounter() { setState(() { if (counter > 0) { counter--; } }); } @override Widget build(BuildContext context) { return Scaffold( bottomSheet: Container( padding: EdgeInsets.all(5.0), child: const Text( 'Some text', style: TextStyle( fontSize: 12.0, ), textAlign: TextAlign.center, ), ), body: RawKeyboardListener( // 复用同一个FocusNode,不要每次build新建 focusNode: _keyboardFocusNode, onKey: (RawKeyEvent event) { // 仅处理按键按下事件,避免重复触发 if (event is RawKeyDownEvent) { if (event.isKeyPressed(LogicalKeyboardKey.enter) || event.isKeyPressed(LogicalKeyboardKey.numpadEnter)) { _incrementCounter(); } if (event.isKeyPressed(LogicalKeyboardKey.backspace)) { _decrementCounter(); } } }, autofocus: true, child: Stack( alignment: Alignment.center, children: <Widget>[ // 原有布局代码 Container( decoration: const BoxDecoration(), ) ], ), ), ); } }
额外优化建议
如果页面不需要一直持有按键监听焦点,可以在页面退到后台或者被覆盖时,主动调用_keyboardFocusNode.unfocus()取消焦点,避免和其他页面的按键监听逻辑冲突。
内容的提问来源于stack exchange,提问作者unacorn
相关产品推荐
相关产品推荐

