Flutter RawKeyboardListener not working with Barcode scanner device

Viewed 2892

Im using an external barcode device for input ( with scan + submit(enter) combination ). I have RawKeyboardListener as TextField parent but it is not working when scanning. It seeing enter key but no barcode and gives some errors;

Platform key support for android is producing unsupported modifier combinations. 'package:flutter/src/services/raw_keyboard.dart': Failed assertion: line 721 pos 11: 'null'

raw_keyboard.dart

final Map<ModifierKey, KeyboardSide> modifiersPressed =
    event.data.modifiersPressed;
final Map<PhysicalKeyboardKey, LogicalKeyboardKey> modifierKeys =
    <PhysicalKeyboardKey, LogicalKeyboardKey>{};
for (final ModifierKey key in modifiersPressed.keys) {
  final Set<PhysicalKeyboardKey> mappedKeys =
      _modifierKeyMap[_ModifierSidePair(key, modifiersPressed[key])];
  assert(
//Line 721;
          mappedKeys != null,
          'Platform key support for ${Platform.operatingSystem} is '
          'producing unsupported modifier combinations.');
      for (final PhysicalKeyboardKey physicalModifier in mappedKeys) {
        modifierKeys[physicalModifier] = _allModifiers[physicalModifier];
      }
    }
    _allModifiersExceptFn.keys.forEach(_keysPressed.remove);
    if (event.data is! RawKeyEventDataFuchsia &&
        event.data is! RawKeyEventDataMacOs) {
      // On Fuchsia and macOS, the Fn key is not considered a modifier key.
      _keysPressed.remove(PhysicalKeyboardKey.fn);
    }
    _keysPressed.addAll(modifierKeys);

Btw barcode scanner working well at google search or other apps

Here is my code;

child: Container(
                  width: 300,
                  height: 35,
                  child: RawKeyboardListener(
                    focusNode: barkodFocus,
                    autofocus: true,
                    onKey: (key) {
                      print(key.toString());
                    },
                    child: TextField(
                      decoration: InputDecoration(
                          contentPadding: EdgeInsets.all(8),
                          border: OutlineInputBorder(
                              borderRadius:
                                  BorderRadius.all(Radius.circular(5)))),
                      textAlign: TextAlign.left,
                      keyboardType: TextInputType.text,
                      readOnly: false,
                      controller: barkodController,
                      onSubmitted: (value) {
                        MusteriSiparisiRowModel satirModel =
                            MusteriSiparisiRowModel();
                        satirModel.setData(value, model).then((sonModel) {
                          setState(() {
                            barkod = value;
                            barkodFocus.requestFocus();
                            barkodController.selection = TextSelection(
                                baseOffset: 0,
                                extentOffset: barkodController.text.length);
                            if (sonModel != null) {
                              satirModel = sonModel;
                              bool add = true;
                              for (var i = 0;
                                  i < satirlarModel.length;
                                  i++) {
                                MusteriSiparisiRowModel m =
                                    satirlarModel[i];
                                if (m.barkod == satirModel.barkod) {
                                  m.setMiktar = m.getMiktar + 1;
                                  add = false;
                                }
                              }
                              if (add == true) {
                                satirlarModel.add(satirModel);
                              }
                            } else {
                              scaffoldKey.currentState
                                  .showSnackBar(SnackBar(
                                      content: Row(
                                children: [
                                  Icon(Icons.announcement),
                                  Padding(padding: EdgeInsets.all(20)),
                                  Text("Barkod Bulunamadı."),
                                ],
                              )));
                            }
                          });
                        });
                      },
                    ),
                  )),

Print(key) works for enter but not for barcode

[√] Flutter (Channel stable, 1.20.2, on Microsoft Windows [Version 10.0.18363.1016], locale tr-TR)
    • Flutter version 1.20.2 at C:\src\flutter\flutter_windows_v1.12.13+hotfix.5-stable\flutter
    • Framework revision bbfbf1770c (3 weeks ago), 2020-08-13 08:33:09 -0700
    • Engine revision 9d5b21729f
    • Dart version 2.9.1

 
[√] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
    • Android SDK at C:\Users\DELL\AppData\Local\Android\sdk
    • Platform android-29, build-tools 29.0.2
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b03)
    • All Android licenses accepted.

[√] Android Studio (version 3.5)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin version 42.1.1
    • Dart plugin version 191.8593
    • Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b03)

[√] VS Code (version 1.48.2)
    • VS Code at C:\Users\DELL\AppData\Local\Programs\Microsoft VS Code
    • Flutter extension version 3.13.2

[√] Connected device (2 available)
    • G0550 (mobile)                     • SAN8PSS5S5PMVEUKRCY • android-arm • Android 4.2.2 (API 17)
    • Android SDK built for x86 (mobile) • emulator-5554       • android-x86 • Android 10 (API 29) (emulator)

• No issues found!
4 Answers

If you want to get barcode as String from RawKeyboardListener, you have to append each character to existing String. RawKeyboardListener listens to "pressed" keys, so you cant insert whole barcode.

There is some working example:

String inputK = "";  
FocusNode focusNode = FocusNode();
RawKeyboardListener(
  autofocus: true,
  focusNode: focusNode,
  onKey: (RawKeyEvent event){
    if(event.runtimeType.toString() == 'RawKeyDownEvent'){
    String key = event.logicalKey.keyLabel;
    if(key != null){
      setState(() {
          inputK += key;
      });
    }
}
  },
  child: Container(),
);

Was there are a solution to this problem? I'm having the same issue with a datalogic memor k scanner. I only receive an event for the last enter. The actual barcode number does not trigger the event.

Updated... I found a setting in the scanner to change the keyboard wedge input mode from text injection to key pressure and then the rawkeyboard listener started working :-)

I shared this code to read barcodes with RawKeyboardListener:

late FocusNode _focusNode;
var barcoder = Barcoder();
RestartableTimer? _timeoutTimer;
RegExp _numeric = RegExp(r'^-?[0-9]+$');
RxString _inputBarcodeText = "".obs;
String _valueBarcode = "";

@override
void initState() {
super.initState();

_focusNode = FocusNode();
_focusNode.requestFocus();

debounce(
  this._inputBarcodeText,
  (dynamic _) {
    // var barcoder = Barcoder(format: "ean13");

    if (_valueBarcode.length > 14) {
      _timeoutTimer?.reset();
      _valueBarcode = '';
      _inputBarcodeText.value = "";
    } else if (_valueBarcode.length >= 8) {
      _timeoutTimer?.reset();

      if (barcoder.validate(_valueBarcode)) {
        if (mounted) processBarcode(_valueBarcode);
      }
    }
  },
  time: Duration(milliseconds: 100),
);
// this clean barcode value after 3 seconds
_timeoutTimer = RestartableTimer(Duration(seconds: 3), () {
  _inputBarcodeText.value = "";
  _valueBarcode = "";
  _timeoutTimer?.reset();
});}
    
void onEventKey(RawKeyEvent keyEvent) {
var data = keyEvent.logicalKey.keyLabel;

if (keyEvent.runtimeType.toString() == 'RawKeyDownEvent' ||  // debug mode
    key.runtimeType.toString() == 'minified:Fy')        // compiled mode
) {
  if (_numeric.hasMatch(data)) {
    _valueBarcode += data;
    // it works with get rx and debounce function, trigger at 100 ms
    _inputBarcodeText.value += data;
    _search = false;
  }
 }
 }

 @override
 Widget build(BuildContext context) => RawKeyboardListener(
    focusNode: _focusNode,
    onKey: onEventKey,
    autofocus: true,
    child: childwidget...,
  );

Maybe it can be useful to someone. The barcoder.validate() is a class which can be found here

You can use this example code for RawKeyboardListener:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'RawKeyboardListener';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: Scaffold(
        appBar: AppBar(title: const Text(_title)),
        body: const MyStatefulWidget(),
      ),
    );
  }
}

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({Key? key}) : super(key: key);

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  final FocusNode _focusNode = FocusNode();

  String _controller = '';

  String? _message;

  @override
  void dispose() {
    _focusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    FocusScope.of(context).requestFocus(_focusNode);
    final TextTheme textTheme = Theme.of(context).textTheme;
    return Container(
      color: Colors.white,
      alignment: Alignment.center,
      child: DefaultTextStyle(
        style: textTheme.bodyText1!,
        child: RawKeyboardListener(
          focusNode: _focusNode,
          onKey: (RawKeyEvent event) {
            if (event is RawKeyDownEvent) {
              if (event.physicalKey == PhysicalKeyboardKey.enter) {
                print('ENTER');
                setState(() {
                  _message = _controller;
                  _controller = '';
                });
              } else {
                print(
                    '_handleKeyEvent Event data keyLabel ${event.data.keyLabel}');
                _controller += event.data.keyLabel;
              }

              print('controller: $_controller');
            }
          },
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                _message ?? 'Press a key',
              ),
              Text(
                '${_message?.length}',
              ),
            ],
          ),
        ),
      ),
    );
  }
}
Related