Flutter 组件集录 | KeyboardListener + CallbackShortcuts

1,321 阅读4分钟

在桌面端开发中,键盘的交互在所难免。Flutter 框架中有 KeyboardListenerCallbackShortcuts 组件,可以让开发者非常方便地 监听键盘事件 以及 处理组合快捷键。本文就来介绍这哥俩,其中案例的代码已经收录到了FlutterUnit 中,可以在对应组件中详细查看。


1. KeyboardListener 组件

FlutterUnit 中的案例很好地说明了 KeyboardListener 组件的功能。该组件需要提供 focusNode 参数用于焦点控制,获取焦点后就可以通过 onKeyEvent 回调监听按键事件:

事件会回调 KeyEvent 对象,他有三种类 KeyDownEvent(按下)、KeyUpEvent(抬起)、KeyRepeatEvent (重复)。上图中每次事件触发会渲染一个盒子,这三个事件类型分别对应 蓝色、灰色、绿色的盒子。

使用方式很简单,只需要将 KeyboardListener 套在目标组件之上,传入 focusNode 控制焦点,就可以通过 onKeyEvent 监听事件了。KeyEvent 中的 logicalKey 可以通过与 LogicalKeyboardKey中的静态成员比较,得到用户按下的键位:

FocusNode focusNode = FocusNode();

KeyboardListener(
  autofocus: true,
  focusNode: focusNode,
  onKeyEvent: _onKeyEvent,
  child: _buildDisplay(),
),

void _onKeyEvent(KeyEvent value) {
  print('${value.runtimeType}:${value.logicalKey.keyLabel}');
  if (value.logicalKey == LogicalKeyboardKey.control) {
    // ctrl 键事件
  }
}

2.CallbackShortcuts 组件

相比于监听键盘按键,在桌面端中直接使用快捷键组合,这种场景更为常见。Flutter 框架中提供了 Shortcuts 组件处理键盘快捷键功能,在之前的 这篇介绍过。这里介绍一下 CallbackShortcuts 组件,它可以更便捷地使用快捷键:
如下面的案例中, 对于计数器的操作有三种快捷键操作: Ctrl+↑ 和 Ctrl+↓ 组合键可以增加或减少数字, Ctrl + R 将数值归零:

从源码中可以看出 CallbackShortcuts 在构造时除了 key 和 child 子组件外,最重要的是 bindings 入参:它是一个 快捷键和无参回调 的映射表,记录哪些组合键,对应的逻辑行为:

ShortcutActivator 抽象类有四个实现类:

如果是单个按键可以使用 SingleActivator ,它在构造时可以传入如下的bool 值,表示和常用按键组合。比如这里的 Ctrl + R 归零,用 SingleActivator(LogicalKeyboardKey.keyR, control: true) 即可。

LogicalKeySet 可以组合最多四个按键,在构造时依次传入即可:


在代码中使用时,通过如下的 _initActionBindings 方法创建组合键和对应函数的映射关系 _actionBindings。可以在状态类 initState 回调中触发该初始方法,在 build 中将 _actionBindings 作为入参,构建 CallbackShortcuts 组件即可。

Map<ShortcutActivator, VoidCallback> _actionBindings = {};

void _initActionBindings() {
  _actionBindings = {
    LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.arrowUp): increase,
    LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.arrowDown): decrease,
    const SingleActivator(LogicalKeyboardKey.keyR, control: true): reset,
  };
}

void increase() {
  setState(() => count = count + 1);
}

void decrease() {
  setState(() => count = count - 1);
}

void reset() {
  setState(() => count = 0);
}

总的来看,KeyboardListener 和 CallbackShortcuts 两个组件的用法还是非常简单的。下面我们来瞄一眼它们的源码实现。


3. 源码实现简看

KeyboardListener 组件的源码非常简单,他就是对 Focus 组件的简单封装,监听 Focus#onKeyEvent 事件,传递给外界。构造函数中传入的属性,也都是为构建 Focus 组件准备的:


而 CallbackShortcuts 组件也是对 Focus 组件的简单封装。也就是说 KeyboardListener 和 CallbackShortcuts 本质上是一个东西。只不过 CallbackShortcuts 对 Focus#onKeyEvent 事件进行了处理,只在组合按键的时机回调而已:

如何监听组合按键,源码中给出了非常明确的方案,在 _applyKeyEventBinding 函数中,通过 ShortcutActivator 对象的 accepts 方法,就可以检测事件是否是当前快捷键组合。如何是,就从映射关系中取回调函数触发:

onKeyEvent: (FocusNode node, KeyEvent event) {
  KeyEventResult result = KeyEventResult.ignored;
  // Activates all key bindings that match, returns "handled" if any handle it.
  for (final ShortcutActivator activator in bindings.keys) {
    result = _applyKeyEventBinding(activator, event) ? KeyEventResult.handled : result;
  }
  return result;
},

bool _applyKeyEventBinding(ShortcutActivator activator, KeyEvent event) {
  if (activator.accepts(event, HardwareKeyboard.instance)) {
    bindings[activator]!.call();
    return true;
  }
  return false;
}

accepts 方法是 ShortcutActivator 类的抽象方法,每个子类有其具体的实现,比如下面是 SingleActivator 的实现:


尾声

总的来看,KeyboardListener 和 CallbackShortcuts 功能性很强,使用起来也很简单那。并且都是基于 Focus 组件实现功能的。至于 Focus 组件是什么,底层是如何实现的,后续会继续跟进,敬请期待~

更多文章和视频知识资讯,大家可以关注我的公众号、掘金和 B 站 。