Flutter如何通过RawKeyboardListener获取条码扫描结果?
实现逻辑说明
安卓设备内置条码扫描模块的输出逻辑,默认是将扫描得到的条码内容模拟成快速连续的键盘输入事件,条码输入完成后会自动追加一个结束标识(绝大多数设备默认是回车符)。RawKeyboardListener 就是通过捕获这串连续按键事件,拼接得到完整条码结果。
核心判断依据:扫码触发的按键输入间隔极短(通常单字符间隔小于100ms),远快于人工手动输入速度,可以通过按键间隔过滤人工输入的干扰,遇到结束符时就得到完整条码。
实现步骤
- 维护3个状态变量:缓存拼接中的扫码内容字符串、记录上一次按键的时间戳、全局持久化的
FocusNode(不要在build方法里临时创建FocusNode,会导致焦点异常、捕获失效,页面销毁时要手动dispose)。 - 按键回调中只处理
RawKeyDownEvent事件,忽略RawKeyUpEvent避免重复捕获字符。 - 每次触发按键事件先判断和上一次按键的时间差,如果间隔超过300ms,判定不属于同一次扫码输入,清空内容缓存。
- 识别到回车结束符时,当前缓存的字符串就是完整条码结果,执行业务逻辑后清空缓存准备下一次扫码。
- 非结束符的普通可打印字符,直接拼接到缓存字符串中即可,过滤掉Shift、Ctrl这类无字符输出的控制按键。
完整示例代码
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class BarcodeScanPage extends StatefulWidget { const BarcodeScanPage({super.key}); @override State<BarcodeScanPage> createState() => _BarcodeScanPageState(); } class _BarcodeScanPageState extends State<BarcodeScanPage> { // 缓存扫码过程中的内容 String _scanBuffer = ''; // 记录上一次按键时间 DateTime? _lastKeyPressTime; // 持久化焦点节点 late final FocusNode _scanFocusNode; // 存储最终扫描结果用于展示 String _finalScanResult = '暂未扫描到条码'; @override void initState() { super.initState(); _scanFocusNode = FocusNode(); } @override void dispose() { _scanFocusNode.dispose(); super.dispose(); } void _handleKeyEvent(RawKeyEvent event) { // 只处理按键按下事件,跳过抬起事件防止重复 if (event is! RawKeyDownEvent) return; final currentTime = DateTime.now(); // 按键间隔超过阈值,判定为非扫码输入,清空缓存 if (_lastKeyPressTime != null && currentTime.difference(_lastKeyPressTime!).inMilliseconds > 300) { _scanBuffer = ''; } _lastKeyPressTime = currentTime; // 捕获到回车结束符,扫码完成 if (event.logicalKey == LogicalKeyboardKey.enter) { if (_scanBuffer.isNotEmpty) { setState(() { _finalScanResult = _scanBuffer; }); // 此处可添加扫码成功的业务逻辑,比如查询商品、跳转页面等 _scanBuffer = ''; } return; } // 拼接普通可打印字符 final inputChar = event.character; if (inputChar != null && inputChar.isNotEmpty) { _scanBuffer += inputChar; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('条码扫描')), body: Center( child: RawKeyboardListener( focusNode: _scanFocusNode, autofocus: true, onKey: _handleKeyEvent, child: Padding( padding: const EdgeInsets.all(16.0), child: Text( _finalScanResult, style: const TextStyle(fontSize: 20), ), ), ), ), ); } }
适配注意事项
- 部分设备可以自定义扫码结束符,如果默认回车无法触发结果回调,可以去设备系统的条码设置页查看结束符配置,修改对应按键判断逻辑即可。
- 如果页面内存在TextField等会抢占输入焦点的组件,需要在扫码场景下保证焦点落在
_scanFocusNode上,否则会无法捕获按键事件。 - 按键间隔阈值可以根据自己设备的扫码速度调整,100ms-500ms区间都可正常使用,既不会截断同一次扫码内容,也能有效过滤人工输入干扰。
内容的提问来源于stack exchange,提问作者VolkanUstekidag
相关产品推荐
相关产品推荐

