Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 29 additions & 84 deletions lib/flutter_barcode_listener.dart
Original file line number Diff line number Diff line change
@@ -1,57 +1,26 @@
library flutter_barcode_listener;

import 'dart:async';

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

typedef BarcodeScannedCallback = void Function(String barcode);

/// This widget will listen for raw PHYSICAL keyboard events
/// even when other controls have primary focus.
/// It will buffer all characters coming in specifed `bufferDuration` time frame
/// that end with line feed character and call callback function with result.
/// Keep in mind this widget will listen for events even when not visible.
/// Windows seems to be using the [RawKeyDownEvent] instead of the
/// [RawKeyUpEvent], this behaviour can be managed by setting [useKeyDownEvent].
class BarcodeKeyboardListener extends StatefulWidget {
final Widget child;
final BarcodeScannedCallback _onBarcodeScanned;
final Duration _bufferDuration;
final bool useKeyDownEvent;

/// Make barcode scanner return case sensitive characters
///
/// Default value is false, It will sent scanned barcode with case sensitive
/// characters. It listen to [LogicalKeyboardKey.shiftLeft]
/// Currently support for Android
final bool caseSensitive;

/// This widget will listen for raw PHYSICAL keyboard events
/// even when other controls have primary focus.
/// It will buffer all characters coming in specifed `bufferDuration` time frame
/// that end with line feed character and call callback function with result.
/// Keep in mind this widget will listen for events even when not visible.
BarcodeKeyboardListener(
{Key? key,

/// Child widget to be displayed.
required this.child,

/// Callback to be called when barcode is scanned.
required Function(String) onBarcodeScanned,

/// When experiencing issueswith empty barcodes on Windows,
/// set this value to true. Default value is `false`.
this.useKeyDownEvent = false,

/// Maximum time between two key events.
/// If time between two key events is longer than this value
/// previous keys will be ignored.
Duration bufferDuration = hundredMs,
this.caseSensitive = false,
})
: _onBarcodeScanned = onBarcodeScanned,
BarcodeKeyboardListener({
Key? key,
required this.child,
required Function(String) onBarcodeScanned,
this.useKeyDownEvent = false,
Duration bufferDuration = hundredMs,
this.caseSensitive = false,
}) : _onBarcodeScanned = onBarcodeScanned,
_bufferDuration = bufferDuration,
super(key: key);

Expand All @@ -71,32 +40,30 @@ class _BarcodeKeyboardListenerState extends State<BarcodeKeyboardListener> {

final BarcodeScannedCallback _onBarcodeScannedCallback;
final Duration _bufferDuration;

final _controller = StreamController<String?>();

final bool _useKeyDownEvent;

final bool _caseSensitive;

bool _isShiftPressed = false;

_BarcodeKeyboardListenerState(this._onBarcodeScannedCallback,
this._bufferDuration, this._useKeyDownEvent, this._caseSensitive) {
RawKeyboard.instance.addListener(_keyBoardCallback);
HardwareKeyboard.instance.addListener(_keyBoardCallback);
_keyboardSubscription =
_controller.stream.where((char) => char != null).listen(onKeyEvent);
}

void onKeyEvent(String? char) {
//remove any pending characters older than bufferDuration value
checkPendingCharCodesToClear();
_lastScannedCharCodeTime = DateTime.now();
if (char == lineFeed) {
_onBarcodeScannedCallback.call(_scannedChars.join());
String scannedCode = _scannedChars.join();
print("Scanned barcode: $scannedCode");
_onBarcodeScannedCallback.call(scannedCode);
resetScannedCharCodes();
} else {
//add character to list of scanned characters;
_scannedChars.add(char!);
if (char != null && RegExp(r'^[a-zA-Z0-9]$').hasMatch(char)) {
_scannedChars.add(char);
}
}
}

Expand All @@ -114,50 +81,28 @@ class _BarcodeKeyboardListenerState extends State<BarcodeKeyboardListener> {
_scannedChars = [];
}

void addScannedCharCode(String charCode) {
_scannedChars.add(charCode);
}
void _keyBoardCallback(KeyboardEvent keyEvent) {
print("Key event: ${keyEvent.character}, Physical key: ${keyEvent.physicalKey}");

void _keyBoardCallback(RawKeyEvent keyEvent) {
if (keyEvent.logicalKey.keyId > 255 &&
keyEvent.data.logicalKey != LogicalKeyboardKey.enter &&
keyEvent.data.logicalKey != LogicalKeyboardKey.shiftLeft) return;
if ((!_useKeyDownEvent && keyEvent is RawKeyUpEvent) ||
(_useKeyDownEvent && keyEvent is RawKeyDownEvent)) {
if (keyEvent.data is RawKeyEventDataAndroid) {
if (keyEvent.data.logicalKey == LogicalKeyboardKey.shiftLeft) {
if (keyEvent.physicalKey.usbHidUsage > 255 &&
keyEvent.physicalKey != PhysicalKeyboardKey.enter &&
keyEvent.physicalKey != PhysicalKeyboardKey.shiftLeft) return;
if ((!_useKeyDownEvent && keyEvent is KeyUpEvent) ||
(_useKeyDownEvent && keyEvent is KeyDownEvent)) {
if (keyEvent is KeyDownEvent) {
if (keyEvent.physicalKey == PhysicalKeyboardKey.shiftLeft) {
_isShiftPressed = true;
} else {
if (_isShiftPressed && _caseSensitive) {
_isShiftPressed = false;
_controller.sink.add(String.fromCharCode(
((keyEvent.data) as RawKeyEventDataAndroid).codePoint).toUpperCase());
_controller.sink.add(keyEvent.character?.toUpperCase());
} else {
_controller.sink.add(String.fromCharCode(
((keyEvent.data) as RawKeyEventDataAndroid).codePoint));
_controller.sink.add(keyEvent.character);
}
}
} else if (keyEvent.data is RawKeyEventDataFuchsia) {
_controller.sink.add(String.fromCharCode(
((keyEvent.data) as RawKeyEventDataFuchsia).codePoint));
} else if (keyEvent.data.logicalKey == LogicalKeyboardKey.enter) {
}
if (keyEvent.physicalKey == PhysicalKeyboardKey.enter) {
_controller.sink.add(lineFeed);
} else if (keyEvent.data is RawKeyEventDataWeb) {
_controller.sink.add(((keyEvent.data) as RawKeyEventDataWeb).keyLabel);
} else if (keyEvent.data is RawKeyEventDataLinux) {
_controller.sink
.add(((keyEvent.data) as RawKeyEventDataLinux).keyLabel);
} else if (keyEvent.data is RawKeyEventDataWindows) {
_controller.sink.add(String.fromCharCode(
((keyEvent.data) as RawKeyEventDataWindows).keyCode));
} else if (keyEvent.data is RawKeyEventDataMacOs) {
_controller.sink
.add(((keyEvent.data) as RawKeyEventDataMacOs).characters);
} else if (keyEvent.data is RawKeyEventDataIos) {
_controller.sink
.add(((keyEvent.data) as RawKeyEventDataIos).characters);
} else {
_controller.sink.add(keyEvent.character);
}
}
}
Expand All @@ -171,7 +116,7 @@ class _BarcodeKeyboardListenerState extends State<BarcodeKeyboardListener> {
void dispose() {
_keyboardSubscription.cancel();
_controller.close();
RawKeyboard.instance.removeListener(_keyBoardCallback);
HardwareKeyboard.instance.removeListener(_keyBoardCallback);
super.dispose();
}
}