You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter不使用TextField如何获取文本输入 实现条码扫描数据捕获处理

你的原有代码失效原因

  1. 在build方法中每次新建FocusNode实例,没有持有实例也没有主动请求焦点,KeyboardListener无法接收系统按键事件
  2. 每次按键直接覆盖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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 18:27:07