diff --git a/README.md b/README.md index 6050aed..973c25c 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,17 @@ When a QR code is recognized, the text identified will be set in 'qrText'. ```dart class _QRViewExampleState extends State { final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); - var qrText = ""; + Barcode result; QRViewController controller; + /// Overriding reassemble and pausing the camera + /// ensures us that hot reload won't give a black screen + @override + void reassemble() { + super.reassemble(); + controller.pauseCamera(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -82,7 +90,9 @@ class _QRViewExampleState extends State { Expanded( flex: 1, child: Center( - child: Text('Scan result: $qrText'), + child: (result != null) ? + Text('Barcode Type: ${describeEnum(result.format)} Data: ${result.code}') + : Text('Scan a code'), ), ) ], diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt index ca59f08..ae23f01 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt @@ -1,6 +1,7 @@ package net.touchcapture.qr.flutterqr import android.Manifest +import android.app.Activity import android.content.pm.PackageManager import android.os.Build import androidx.annotation.NonNull @@ -29,22 +30,24 @@ class FlutterQrPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } private fun onAttachedToV1(registrar: PluginRegistry.Registrar) { - Shared.activity = registrar.activity() registrar.addRequestPermissionsResultListener(CameraRequestPermissionsListener()) checkAndRequestPermission(null) - onAttachedToEngines(registrar.platformViewRegistry(), registrar.messenger()) + onAttachedToEngines(registrar.platformViewRegistry(), registrar.messenger(), registrar.activity()) } /** Plugin registration embedding v2 */ override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - onAttachedToEngines(flutterPluginBinding.platformViewRegistry, flutterPluginBinding.binaryMessenger) + onAttachedToEngines(flutterPluginBinding.platformViewRegistry, flutterPluginBinding.binaryMessenger, Shared.activity) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { } /** Plugin start for both embedding v1 & v2 */ - private fun onAttachedToEngines(platformViewRegistry: PlatformViewRegistry, messenger: BinaryMessenger) { + private fun onAttachedToEngines(platformViewRegistry: PlatformViewRegistry, messenger: BinaryMessenger, activity: Activity?) { + if (activity != null) { + Shared.activity = activity + } platformViewRegistry .registerViewFactory( "net.touchcapture.qr.flutterqr/qrview", QRViewFactory(messenger)) diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt index ede6fc2..e91743d 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt @@ -116,7 +116,8 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context) barcode.decodeContinuous( object : BarcodeCallback { override fun barcodeResult(result: BarcodeResult) { - channel.invokeMethod("onRecognizeQR", result.text) + val code = mapOf("code" to result.text, "type" to result.barcodeFormat.name) + channel.invokeMethod("onRecognizeQR", code) } override fun possibleResultPoints(resultPoints: List) {} diff --git a/example/README.md b/example/README.md index 4fc9d62..866b9a4 100644 --- a/example/README.md +++ b/example/README.md @@ -41,12 +41,20 @@ class QRViewExample extends StatefulWidget { } class _QRViewExampleState extends State { - var qrText = ''; + Barcode result; var flashState = flashOn; var cameraState = frontCamera; QRViewController controller; final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); + /// Overriding reassemble and pausing the camera + /// ensures us that hot reload won't give a black screen + @override + void reassemble() { + super.reassemble(); + controller.pauseCamera(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -60,7 +68,10 @@ class _QRViewExampleState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - Text('This is the result of scan: $qrText'), + if (result != null) + Text('Barcode Type: ${describeEnum(result.format)} Data: ${result.code}') + else + Text('Scan a code'), Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, @@ -177,7 +188,7 @@ class _QRViewExampleState extends State { this.controller = controller; controller.scannedDataStream.listen((scanData) { setState(() { - qrText = scanData; + result = scanData; }); }); } @@ -189,6 +200,7 @@ class _QRViewExampleState extends State { } } + ``` diff --git a/example/lib/main.dart b/example/lib/main.dart index 4facbeb..4d5680c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; @@ -18,12 +19,18 @@ class QRViewExample extends StatefulWidget { } class _QRViewExampleState extends State { - var qrText = ''; + Barcode result; var flashState = flashOn; var cameraState = frontCamera; QRViewController controller; final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); + @override + void reassemble() { + super.reassemble(); + controller.pauseCamera(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -37,7 +44,11 @@ class _QRViewExampleState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - Text('This is the result of scan: $qrText'), + if (result != null) + Text( + 'Barcode Type: ${describeEnum(result.format)} Data: ${result.code}') + else + Text('Scan a code'), Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, @@ -154,7 +165,7 @@ class _QRViewExampleState extends State { this.controller = controller; controller.scannedDataStream.listen((scanData) { setState(() { - qrText = scanData; + result = scanData; }); }); } diff --git a/ios/Classes/QRView.swift b/ios/Classes/QRView.swift index 55426f0..eeb3bfd 100644 --- a/ios/Classes/QRView.swift +++ b/ios/Classes/QRView.swift @@ -26,8 +26,37 @@ public class QRView:NSObject,FlutterPlatformView { try scanner?.startScanning(resultBlock: { [weak self] codes in if let codes = codes { for code in codes { + var typeString: String; + switch(code.type) { + case AVMetadataObject.ObjectType.aztec: + typeString = "AZTEC" + case AVMetadataObject.ObjectType.code39: + typeString = "CODE_39" + case AVMetadataObject.ObjectType.code93: + typeString = "CODE_93" + case AVMetadataObject.ObjectType.code128: + typeString = "CODE_128" + case AVMetadataObject.ObjectType.dataMatrix: + typeString = "DATA_MATRIX" + case AVMetadataObject.ObjectType.ean8: + typeString = "EAN_8" + case AVMetadataObject.ObjectType.ean13: + typeString = "EAN_13" + case AVMetadataObject.ObjectType.itf14: + typeString = "ITF" + case AVMetadataObject.ObjectType.pdf417: + typeString = "PDF_417" + case AVMetadataObject.ObjectType.qr: + typeString = "QR_CODE" + case AVMetadataObject.ObjectType.upce: + typeString = "UPC_E" + default: + return + } + guard let stringValue = code.stringValue else { continue } - self?.channel.invokeMethod("onRecognizeQR", arguments: stringValue) + let result = ["code": stringValue, "type": typeString] + self?.channel.invokeMethod("onRecognizeQR", arguments: result) } } }) diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index 70a96a4..451f240 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -6,6 +6,86 @@ import 'package:flutter/services.dart'; typedef QRViewCreatedCallback = void Function(QRViewController); +enum BarcodeFormat { + /// Aztec 2D barcode format. + aztec, + + /// CODABAR 1D format. + codabar, + + /// Code 39 1D format. + code39, + + /// Code 93 1D format. + code93, + + /// Code 128 1D format. + code128, + + /// Data Matrix 2D barcode format. + dataMatrix, + + /// EAN-8 1D format. + ean8, + + /// EAN-13 1D format. + ean13, + + /// ITF (Interleaved Two of Five) 1D format. + itf, + + /// MaxiCode 2D barcode format. + maxicode, + + /// PDF417 format. + pdf417, + + /// QR Code 2D barcode format. + qrcode, + + /// RSS 14 + rss14, + + /// RSS EXPANDED + rssExpanded, + + /// UPC-A 1D format. + upcA, + + /// UPC-E 1D format. + upcE, + + /// UPC/EAN extension format. Not a stand-alone format. + upcEanExtension +} + +const _formatNames = { + 'AZTEC': BarcodeFormat.aztec, + 'CODABAR': BarcodeFormat.codabar, + 'CODE_39': BarcodeFormat.code39, + 'CODE_93': BarcodeFormat.code93, + 'CODE_128': BarcodeFormat.code128, + 'DATA_MATRIX': BarcodeFormat.dataMatrix, + 'EAN_8': BarcodeFormat.ean8, + 'EAN_13': BarcodeFormat.ean13, + 'ITF': BarcodeFormat.itf, + 'MAXICODE': BarcodeFormat.maxicode, + 'PDF_417': BarcodeFormat.pdf417, + 'QR_CODE': BarcodeFormat.qrcode, + 'RSS_14': BarcodeFormat.rss14, + 'RSS_EXPANDED': BarcodeFormat.rssExpanded, + 'UPC_A': BarcodeFormat.upcA, + 'UPC_E': BarcodeFormat.upcE, + 'UPC_EAN_EXTENSION': BarcodeFormat.upcEanExtension, +}; + +class Barcode { + Barcode(this.code, this.format); + + final String code; + final BarcodeFormat format; +} + class QRView extends StatefulWidget { const QRView({ @required Key key, @@ -106,7 +186,16 @@ class QRViewController { switch (call.method) { case scanMethodCall: if (call.arguments != null) { - _scanUpdateController.sink.add(call.arguments.toString()); + final args = call.arguments as Map; + final code = args['code'] as String; + final rawType = args['type'] as String; + final format = _formatNames[rawType]; + if (format != null) { + final barcode = Barcode(code, format); + _scanUpdateController.sink.add(barcode); + } else { + throw Exception('Unexpected barcode type $rawType'); + } } } }, @@ -117,10 +206,10 @@ class QRViewController { final MethodChannel _channel; - final StreamController _scanUpdateController = - StreamController(); + final StreamController _scanUpdateController = + StreamController(); - Stream get scannedDataStream => _scanUpdateController.stream; + Stream get scannedDataStream => _scanUpdateController.stream; void flipCamera() { _channel.invokeMethod('flipCamera');