Search code examples
flutterbuffertextfieldbarcodebarcode-scanner

Flutter Rapid input into TextField


I am using a handheld barcode scanner to scan a barcode. I couldn't get it to work without tracking keyboard input. It now works MOST of the time, but sometimes if I scan #COUNTING-TEST-TEST it will miss letters and result in something like #COUN-T.

Barcode type is Code-128 and scanner is Zebra DS2208

I am using an Amazon Fire Tablet, If I scan on desktop it works perfectly.

Has anyone had success with this?

class CustomTextFieldState extends State<CustomTextField> {
  String _inputBuffer = ''; // Buffer to accumulate input
  Timer? _bufferTimer; // Timer for clearing buffer

  // Method to manually trigger onSubmitted
  void submit() {
    if (widget.onSubmitted != null) {
      widget.onSubmitted!(widget.controller.text);
      widget.controller.clear();
      _inputBuffer = "";
      widget.focusNode.requestFocus();
    }
  }

  KeyEventResult _handleKeyEvent(FocusNode node, KeyEvent event) {
    if (event is KeyDownEvent) {
      final LogicalKeyboardKey logicalKey = event.logicalKey;
      final String key = event.character ?? '';

      // Handle Enter key only when LogicalKeyboardKey.enter is pressed
      if (logicalKey == LogicalKeyboardKey.enter) { 
        // Call onSubmitted with buffered input
        widget.onSubmitted?.call(_inputBuffer);
        
        // Clear the controller text and input buffer
        widget.controller.clear();
        _inputBuffer = ''; 
        widget.focusNode.requestFocus();
        return KeyEventResult.handled;
      }

      // Handle Backspace key
      else if (logicalKey == LogicalKeyboardKey.backspace) {
        if (_inputBuffer.isNotEmpty) {
          // Remove the last character from the buffer
          _inputBuffer = _inputBuffer.substring(0, _inputBuffer.length - 1);
          widget.controller.text = _inputBuffer;
        }
        return KeyEventResult.handled;
      }

      // Handle normal characters: add them to the buffer and update the controller
      else if (key.isNotEmpty && key.length == 1) {
        // Add the character to the buffer and update the controller text
        _inputBuffer += key;
        widget.controller.text = _inputBuffer;  // Reflect the key in the text field
        return KeyEventResult.handled;
      }
    }
    return KeyEventResult.ignored;
  }

  void _resetBufferTimer() {
    _bufferTimer?.cancel();
    _bufferTimer = Timer(widget.bufferDuration, () {
      setState(() {
        _inputBuffer = ''; // Clear buffer if no input for bufferDuration
      });
    });
  }

  @override
  void dispose() {
    _bufferTimer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Focus(
      focusNode: widget.focusNode,
      onKeyEvent: _handleKeyEvent, // Handle key events here
      child: TextField(
        autocorrect: false,
        autofocus: true,
        textInputAction: TextInputAction.done,
        controller: widget.controller,
        textCapitalization: TextCapitalization.none,
        decoration: InputDecoration(
          labelText: widget.labelText,
          border: const OutlineInputBorder(),
          prefixIcon: IconButton(
            icon: Icon(Icons.clear),
            onPressed: () {
              widget.controller.clear();
              setState(() {
                _inputBuffer = ''; // Clear the buffer as well
              });
            },
          )
        ),
        
      ),
    );
  }
}

I have tried using stream_transform package, queues, buffers. I even tried registering native android edit text if Platform is android, but that came with its own complications, although it was able to read the rapid input very well.


Solution

  • So I was finally able to get it. I used the flutter_barcode_listener package with a little bit of customization.

    
        void _handleBarcodeScan(String scannedCode) async {
        if (!widget.focusNode.hasFocus) {
          return;
        }
        setState(() {
          // scannedcode came in with extra whitespace
          _scannedText = scannedCode.trim();
          
          // Scanner configured with enter after scan came in with `a` at the end
          if (_scannedText.endsWith("`a`")) {
            // # was transformed to 3 I needed to only remove the first 3 since 
               that is the only spot # will be
            _scannedText = _scannedText.replaceFirst("3", "#");
          }
          
          // If user manually entered text then often times _scannedText would be 
          // empty so instead I got the text from the widget
          if (_scannedText == "" && widget.controller.text != ""){
            _scannedText = widget.controller.text.toUpperCase();
          }
    
          // Replace all specific characters as needed
          _scannedText = _scannedText
              .replaceAll("½", "-")
              .replaceAll("``a`", "")
              .replaceAll(RegExp(r"\s+"), "") // Remove all spaces, including multiple spaces
              .replaceAll("¡3", "#"); // # would sometimes come in as that weird 
                                      // character as well
        });
    
        
        widget.controller.clear();
        widget.onBarcodeComplete(_scannedText);
      }
    
        @override
      Widget build(BuildContext context) {
        return BarcodeKeyboardListener(
          bufferDuration: widget.bufferDuration,
          onBarcodeScanned: _handleBarcodeScan,
          child: TextField(
            controller: widget.controller,
            focusNode: widget.focusNode,
            textCapitalization: TextCapitalization.none,
            decoration: InputDecoration(
              labelText: widget.labelText,
              border: const OutlineInputBorder(),
              prefixIcon: IconButton(
                icon: Icon(Icons.clear),
                onPressed: () {
                  widget.controller.clear();
                  setState(() {
                    _scannedText = '';
                  });
                },
              ),
            ),
            onChanged: _onTextChanged,
            onSubmitted: (value) {
            },
          ),
        );
      }
    }