Julian Steenbakker
Committed by GitHub

Merge pull request #410 from p-mazhnik/pavel/web-format

feat(web): support formats and detectionTimeout arguments
... ... @@ -5,7 +5,9 @@ import 'dart:ui' as ui;
import 'package:flutter/services.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:mobile_scanner/mobile_scanner_web.dart';
import 'package:mobile_scanner/src/barcode_utility.dart';
import 'package:mobile_scanner/src/enums/camera_facing.dart';
import 'package:mobile_scanner/src/objects/barcode.dart';
/// This plugin is the web implementation of mobile_scanner.
/// It only supports QR codes.
... ... @@ -102,8 +104,23 @@ class MobileScannerWebPlugin {
};
}
try {
List<BarcodeFormat>? formats;
if (arguments.containsKey('formats')) {
formats = (arguments['formats'] as List)
.cast<int>()
.map((e) => toFormat(e))
.toList();
}
final Duration? detectionTimeout;
if (arguments.containsKey('timeout')) {
detectionTimeout = Duration(milliseconds: arguments['timeout'] as int);
} else {
detectionTimeout = null;
}
await barCodeReader.start(
cameraFacing: cameraFacing,
formats: formats,
detectionTimeout: detectionTimeout,
);
_barCodeStreamSubscription =
... ... @@ -114,6 +131,7 @@ class MobileScannerWebPlugin {
'data': {
'rawValue': code.rawValue,
'rawBytes': code.rawBytes,
'format': code.format.rawValue,
},
});
}
... ...
... ... @@ -136,10 +136,10 @@ class MobileScannerController {
} */
if (formats != null) {
if (Platform.isAndroid) {
arguments['formats'] = formats!.map((e) => e.index).toList();
} else if (Platform.isIOS || Platform.isMacOS) {
if (kIsWeb || Platform.isIOS || Platform.isMacOS) {
arguments['formats'] = formats!.map((e) => e.rawValue).toList();
} else if (Platform.isAndroid) {
arguments['formats'] = formats!.map((e) => e.index).toList();
}
}
arguments['returnImage'] = true;
... ... @@ -363,10 +363,12 @@ class MobileScannerController {
_barcodesController.add(
BarcodeCapture(
barcodes: [
Barcode(
rawValue: barcode?['rawValue'] as String?,
rawBytes: barcode?['rawBytes'] as Uint8List?,
)
if (barcode != null)
Barcode(
rawValue: barcode['rawValue'] as String?,
rawBytes: barcode['rawBytes'] as Uint8List?,
format: toFormat(barcode['format'] as int),
),
],
),
);
... ...
... ... @@ -9,12 +9,11 @@ import 'package:mobile_scanner/src/web/media.dart';
abstract class WebBarcodeReaderBase {
/// Timer used to capture frames to be analyzed
final Duration frameInterval;
Duration frameInterval = const Duration(milliseconds: 200);
final html.DivElement videoContainer;
const WebBarcodeReaderBase({
WebBarcodeReaderBase({
required this.videoContainer,
this.frameInterval = const Duration(milliseconds: 200),
});
bool get isStarted;
... ... @@ -25,6 +24,8 @@ abstract class WebBarcodeReaderBase {
/// Starts streaming video
Future<void> start({
required CameraFacing cameraFacing,
List<BarcodeFormat>? formats,
Duration? detectionTimeout,
});
/// Starts scanning QR codes or barcodes
... ... @@ -158,3 +159,14 @@ class ImageCapture {
extension ImageCaptureExt on ImageCapture {
external Promise<PhotoCapabilities> getPhotoCapabilities();
}
@JS('Map')
@staticInterop
class JsMap {
external factory JsMap();
}
extension JsMapExt on JsMap {
external void set(dynamic key, dynamic value);
external dynamic get(dynamic key);
}
... ...
... ... @@ -35,9 +35,15 @@ class JsQrCodeReader extends WebBarcodeReaderBase
@override
Future<void> start({
required CameraFacing cameraFacing,
List<BarcodeFormat>? formats,
Duration? detectionTimeout,
}) async {
videoContainer.children = [video];
if (detectionTimeout != null) {
frameInterval = detectionTimeout;
}
final stream = await initMediaStream(cameraFacing);
prepareVideoElement(video);
... ...
... ... @@ -43,12 +43,14 @@ extension ResultExt on Result {
/// https://github.com/zxing-js/library/blob/1e9ccb3b6b28d75b9eef866dba196d8937eb4449/src/core/BarcodeFormat.ts#L28
BarcodeFormat get barcodeFormat {
switch (format) {
case 1:
case 0:
return BarcodeFormat.aztec;
case 2:
case 1:
return BarcodeFormat.codebar;
case 3:
case 2:
return BarcodeFormat.code39;
case 3:
return BarcodeFormat.code93;
case 4:
return BarcodeFormat.code128;
case 5:
... ... @@ -79,6 +81,42 @@ extension ResultExt on Result {
}
}
extension ZXingBarcodeFormat on BarcodeFormat {
int get zxingBarcodeFormat {
switch (this) {
case BarcodeFormat.aztec:
return 0;
case BarcodeFormat.codebar:
return 1;
case BarcodeFormat.code39:
return 2;
case BarcodeFormat.code93:
return 3;
case BarcodeFormat.code128:
return 4;
case BarcodeFormat.dataMatrix:
return 5;
case BarcodeFormat.ean8:
return 6;
case BarcodeFormat.ean13:
return 7;
case BarcodeFormat.itf:
return 8;
case BarcodeFormat.pdf417:
return 10;
case BarcodeFormat.qrCode:
return 11;
case BarcodeFormat.upcA:
return 14;
case BarcodeFormat.upcE:
return 15;
case BarcodeFormat.unknown:
case BarcodeFormat.all:
return -1;
}
}
}
typedef BarcodeDetectionCallback = void Function(
Result? result,
dynamic error,
... ... @@ -136,11 +174,7 @@ extension JsZXingBrowserMultiFormatReaderExt
/// <script type="text/javascript" src="https://unpkg.com/@zxing/library@0.19.1"></script>
class ZXingBarcodeReader extends WebBarcodeReaderBase
with InternalStreamCreation, InternalTorchDetection {
late final JsZXingBrowserMultiFormatReader _reader =
JsZXingBrowserMultiFormatReader(
null,
frameInterval.inMilliseconds,
);
JsZXingBrowserMultiFormatReader? _reader;
ZXingBarcodeReader({required super.videoContainer});
... ... @@ -150,7 +184,27 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
@override
Future<void> start({
required CameraFacing cameraFacing,
List<BarcodeFormat>? formats,
Duration? detectionTimeout,
}) async {
final JsMap? hints;
if (formats != null && !formats.contains(BarcodeFormat.all)) {
hints = JsMap();
final zxingFormats =
formats.map((e) => e.zxingBarcodeFormat).where((e) => e > 0).toList();
// set hint DecodeHintType.POSSIBLE_FORMATS
// https://github.com/zxing-js/library/blob/1e9ccb3b6b28d75b9eef866dba196d8937eb4449/src/core/DecodeHintType.ts#L28
hints.set(2, zxingFormats);
} else {
hints = null;
}
if (detectionTimeout != null) {
frameInterval = detectionTimeout;
}
_reader = JsZXingBrowserMultiFormatReader(
hints,
frameInterval.inMilliseconds,
);
videoContainer.children = [video];
final stream = await initMediaStream(cameraFacing);
... ... @@ -163,7 +217,7 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
@override
void prepareVideoElement(VideoElement videoSource) {
_reader.prepareVideoElement(videoSource);
_reader?.prepareVideoElement(videoSource);
}
@override
... ... @@ -171,9 +225,9 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
MediaStream stream,
VideoElement videoSource,
) async {
_reader.addVideoSource(videoSource, stream);
_reader.videoElement = videoSource;
_reader.stream = stream;
_reader?.addVideoSource(videoSource, stream);
_reader?.videoElement = videoSource;
_reader?.stream = stream;
localMediaStream = stream;
await videoSource.play();
}
... ... @@ -182,7 +236,7 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
Stream<Barcode?> detectBarcodeContinuously() {
final controller = StreamController<Barcode?>();
controller.onListen = () async {
_reader.decodeContinuously(
_reader?.decodeContinuously(
video,
allowInterop((result, error) {
if (result != null) {
... ... @@ -192,14 +246,14 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
);
};
controller.onCancel = () {
_reader.stopContinuousDecode();
_reader?.stopContinuousDecode();
};
return controller.stream;
}
@override
Future<void> stop() async {
_reader.reset();
_reader?.reset();
super.stop();
}
}
... ...