diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..60ae300 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,51 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "camcode", + "request": "launch", + "type": "dart" + }, + { + "name": "camcode (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "camcode (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + }, + { + "name": "example (web)", + "cwd": "example", + "request": "launch", + "type": "dart", + "args": [ + "-d", + "chrome", + "--web-port=3000", + "--web-hostname=0.0.0.0", + ] + }, + { + "name": "example (profile mode)", + "cwd": "example", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "example (release mode)", + "cwd": "example", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 917ffdf..19d8d87 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ showDialog( onBarcodeResult: (barcode) { // do whatever you want }, + minimalResultCount: 2, ), ); ``` diff --git a/example/lib/main.dart b/example/lib/main.dart index 044cfe4..550dbaa 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -101,7 +101,8 @@ class _CamCodeScannerPageState extends State { widget.onResult(barcode); }, controller: _controller, - showDebugFrames: true, + showDebugFrames: false, + minimalResultCount: 1, ), Positioned( bottom: 48.0, diff --git a/example/pubspec.lock b/example/pubspec.lock index d958390..21d8394 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -21,7 +21,7 @@ packages: path: ".." relative: true source: path - version: "2.2.1" + version: "3.0.0" characters: dependency: transitive description: @@ -169,5 +169,5 @@ packages: source: hosted version: "2.1.2" sdks: - dart: ">=2.17.0-0 <3.0.0" + dart: ">=2.17.0 <3.0.0" flutter: ">=1.22.6" diff --git a/lib/barcode_results.dart b/lib/barcode_results.dart index 641c422..a023e21 100644 --- a/lib/barcode_results.dart +++ b/lib/barcode_results.dart @@ -1,11 +1,21 @@ /// Store and count every potential result /// Once a barcode was identified minimalResultCount times, it is considered as a valid result class BarcodeResults { - static const minimalResultCount = 2; + /// Set to true if you want to get the first scanned value + /// with no concordance check + bool singleShot; + + /// Number of identic scanned value to get before consider getting result + int minimalResultCount; /// list of barcode results final Map _barcodeResults = {}; + BarcodeResults({ + this.singleShot = false, + this.minimalResultCount = 2, + }); + /// clears all barcode results void clear() { _barcodeResults.clear(); @@ -15,7 +25,14 @@ class BarcodeResults { int get resultCount => _barcodeResults.values.reduce((a, b) => a + b); /// Consider that we have a barcode result once enough identic results are found - bool get gotResult => + bool get gotResult => _containsSingleShotResult || _containesConcordance; + + /// return true if singleShot mode is enabled and resultCount is positive + bool get _containsSingleShotResult => singleShot && resultCount > 0; + + /// return true if results contains at least minimalResultCount + /// of the same barcode + bool get _containesConcordance => resultCount >= 2 && _barcodeResults.values.any( (singleBarcodeCount) => singleBarcodeCount >= minimalResultCount, diff --git a/lib/cam_code_scanner.dart b/lib/cam_code_scanner.dart index 06c3132..f5a7002 100644 --- a/lib/cam_code_scanner.dart +++ b/lib/cam_code_scanner.dart @@ -33,6 +33,9 @@ class CamCodeScanner extends StatefulWidget { /// shows the current analysing picture final bool showScannerLine; + /// Number of identic scanned value to get before consider getting result + final int minimalResultCount; + /// Camera barcode scanner widget /// Params: /// * showDebugFrames [true|false] - shows the current analysing picture @@ -48,6 +51,7 @@ class CamCodeScanner extends StatefulWidget { this.controller, this.backgroundColor = Colors.black54, this.showScannerLine = true, + this.minimalResultCount = 2, }); @override @@ -93,6 +97,7 @@ class _CamCodeScannerState extends State { widget.width, widget.height, widget.refreshDelayMillis, + widget.minimalResultCount, ], ); diff --git a/lib/camcode_web.dart b/lib/camcode_web.dart index 8a5ca11..7c0a12c 100644 --- a/lib/camcode_web.dart +++ b/lib/camcode_web.dart @@ -69,6 +69,7 @@ class CamcodeWeb { arguments[0], arguments[1], arguments[2], + arguments[3], ); case 'releaseResources': return releaseResources(); @@ -107,9 +108,12 @@ class CamcodeWeb { double width, double height, int refreshDelayMillis, + int minimalResultCount, ) { completer = Completer(); _enumerateDevicesCompleter = Completer>(); + _barcodeResults.singleShot = minimalResultCount == 1; + _barcodeResults.minimalResultCount = minimalResultCount; _barcodeResults.clear(); // Create a video element which will be provided with stream source diff --git a/pubspec.lock b/pubspec.lock index 4c2a0c5..5d5487d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -43,6 +43,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.16.0" + every_test: + dependency: "direct dev" + description: + name: every_test + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" fake_async: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2a0e87b..0f2922c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: camcode description: A camera barcode scanner for Flutter Web using your favorite Javascript library -version: 3.0.0-SNAPSHOT +version: 3.0.0 repository: https://github.com/PiotrFLEURY/camcode homepage: https://camcode-59c70.web.app/ @@ -26,6 +26,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + every_test: ^1.0.0 pedantic: ^1.9.0 environment: diff --git a/test/barcode_results_test.dart b/test/barcode_results_test.dart index 49c43c8..3a3492f 100644 --- a/test/barcode_results_test.dart +++ b/test/barcode_results_test.dart @@ -1,4 +1,5 @@ import 'package:camcode/barcode_results.dart'; +import 'package:every_test/every_test.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -75,4 +76,82 @@ void main() { expect(mostFrequent, '123456789'); }); }); + + group('singleShot', () { + test('singleShot enabled', () { + // GIVEN + final barcodeResults = BarcodeResults(singleShot: true); + barcodeResults.add('42424242'); + + // WHEN + final gotResult = barcodeResults.gotResult; + + // THEN + expect(gotResult, true); + }); + + test('singleShot disabled', () { + // GIVEN + final barcodeResults = BarcodeResults(singleShot: false); + barcodeResults.add('42424242'); + + // WHEN + final gotResult = barcodeResults.gotResult; + + // THEN + expect(gotResult, false); + }); + + test('singleShot default', () { + // GIVEN + final barcodeResults = BarcodeResults(); + barcodeResults.add('42424242'); + + // WHEN + final gotResult = barcodeResults.gotResult; + + // THEN + expect(gotResult, false); + }); + }); + + everyTest( + 'minimalResultCount', + of: (params) { + // GIVEN + final _minimalResulCount = params['minimalResultCount'] as int; + final List barcodes = params['barcodes']; + final barcodeResults = + BarcodeResults(minimalResultCount: _minimalResulCount); + barcodes.forEach((it) { + barcodeResults.add(it); + }); + + // WHEN + return barcodeResults.gotResult; + }, + expects: [ + // THEN + param({ + 'minimalResultCount': 1, + 'barcodes': ['123456'] + }).gives(false), + param({ + 'minimalResultCount': 2, + 'barcodes': ['123456', '123456'] + }).gives(true), + param({ + 'minimalResultCount': 2, + 'barcodes': ['123456', '123456', '123456'] + }).gives(true), + param({ + 'minimalResultCount': 2, + 'barcodes': ['123456'] + }).gives(false), + param({ + 'minimalResultCount': 4, + 'barcodes': ['123456', '123456', '123456'] + }).gives(false), + ], + ); }