Flutter不使用TextField如何获取文本输入 实现条码扫描数据捕获处理
你的原有代码失效原因
- 在build方法中每次新建
FocusNode实例,没有持有实例也没有主动请求焦点,KeyboardListener无法接收系统按键事件 - 每次按键直接覆盖
text变量,只会保留最后一个输入的字符,扫码枪本质是模拟键盘连续输入一串字符,需要先缓存再整体处理
方案1:改进KeyboardListener实现
改造成本最低,适配绝大多数默认模拟键盘输入、结束时输出回车的扫码枪设备:
class _YourPageState extends State<YourPage> { // 全局持有FocusNode实例 late final FocusNode _focusNode; // 扫码字符缓冲区 String _scanBuffer = ''; String text = ''; @override void initState() { super.initState(); _focusNode = FocusNode(); // 主动请求焦点,才能接收按键事件 _focusNode.requestFocus(); } @override void dispose() { _focusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return KeyboardListener( focusNode: _focusNode, autofocus: true, onKeyEvent: (event) { // 只处理按键按下事件,避免重复触发 if (event is KeyDownEvent) { // 扫码枪结束通常会输入回车,以此判断扫码完成 if (event.logicalKey == LogicalKeyboardKey.enter) { // 处理扫描结果 text = _scanBuffer; // 调用你的购物车更新逻辑 _handleScanResult(_scanBuffer); // 清空缓冲区准备下一次扫码 _scanBuffer = ''; setState(() {}); } else if (event.character != null) { // 拼接输入的字符到缓冲区 _scanBuffer += event.character!; } } }, child: Scaffold( appBar: AppBar( title: const Text('Retrieve Text Input'), ), body: Center( child: Text(text), ), ), ); } void _handleScanResult(String scanCode) { // 购物车更新逻辑写在这里 } }
方案2:使用TextInputConnection实现(更稳定)
完全模拟系统文本输入的接收逻辑,不会因为页面焦点被其他控件抢占影响扫码捕获,适配所有输入场景:
// State类实现TextInputClient接口 class _YourPageState extends State<YourPage> implements TextInputClient { TextInputConnection? _textInputConnection; String text = ''; @override void initState() { super.initState(); _initTextInput(); } void _initTextInput() { // 先关闭已存在的连接 _textInputConnection?.close(); // 建立和系统输入的连接 _textInputConnection = TextInput.attach( this, const TextInputConfiguration( // 扫码是数字可指定number类型,也可改为text适配所有字符 inputType: TextInputType.number, ), ); // 激活输入连接 _textInputConnection?.show(); // 设置初始输入内容为空 _textInputConnection?.setEditingState(const TextEditingValue(text: '')); } // 核心回调:输入内容变化时自动触发 @override void updateEditingValue(TextEditingValue value) { final inputText = value.text; // 扫码结束判断:可根据你的条码规则调整,比如固定长度、包含特定前缀/后缀 if (inputText.endsWith('\n') || inputText.length >= 8) { final scanCode = inputText.replaceAll('\n', '').trim(); text = scanCode; _handleScanResult(scanCode); // 清空输入,准备下一次扫码 _textInputConnection?.setEditingState(const TextEditingValue(text: '')); setState(() {}); } } // 以下是TextInputClient要求实现的默认方法,无需业务逻辑直接保留即可 @override void performAction(TextInputAction action) {} @override void connectionClosed() {} @override AutofillScope? get currentAutofillScope => null; @override TextEditingValue? get currentTextEditingValue => null; @override void insertContent(KeyboardInsertedContent content) {} @override void insertTextPlaceholder(Size size) {} @override void performPrivateCommand(String action, Map<String, dynamic> data) {} @override void removeTextPlaceholder() {} @override void showAutocorrectionPromptRect(int start, int end) {} @override void showToolbar() {} @override void updateFloatingCursor(RawFloatingCursorPoint point) {} @override void didChangeInputControl(TextInputControl? oldControl, TextInputControl? newControl) {} @override void performSelector(String selectorName) {} @override void dispose() { _textInputConnection?.close(); super.dispose(); } void _handleScanResult(String scanCode) { // 购物车更新逻辑写在这里 } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Retrieve Text Input'), ), body: Center( child: Text(text), ), ); } }
注意事项
如果页面有其他可输入控件,其他控件获取焦点时会中断输入连接,你需要在需要重新捕获扫码的时候调用_initTextInput()重新建立连接即可。
内容的提问来源于stack exchange,提问作者Yves Boutellier
相关产品推荐
相关产品推荐

