86 select camera on startflutter-beta
| @@ -63,7 +63,6 @@ class FlutterQrPlugin : FlutterPlugin, ActivityAware { | |||||
| inner class CameraRequestPermissionsListener : PluginRegistry.RequestPermissionsResultListener { | inner class CameraRequestPermissionsListener : PluginRegistry.RequestPermissionsResultListener { | ||||
| override fun onRequestPermissionsResult(id: Int, permissions: Array<String>, grantResults: IntArray): Boolean { | override fun onRequestPermissionsResult(id: Int, permissions: Array<String>, grantResults: IntArray): Boolean { | ||||
| if (id == Shared.CAMERA_REQUEST_ID && grantResults[0] == PackageManager.PERMISSION_GRANTED) { | if (id == Shared.CAMERA_REQUEST_ID && grantResults[0] == PackageManager.PERMISSION_GRANTED) { | ||||
| Shared.cameraPermissionContinuation?.run() | |||||
| return true | return true | ||||
| } | } | ||||
| return false | return false | ||||
| @@ -3,13 +3,13 @@ package net.touchcapture.qr.flutterqr | |||||
| import android.Manifest | import android.Manifest | ||||
| import android.app.Activity | import android.app.Activity | ||||
| import android.app.Application | import android.app.Application | ||||
| import android.content.Context | |||||
| import android.content.pm.PackageManager | import android.content.pm.PackageManager | ||||
| import android.os.Bundle | import android.os.Bundle | ||||
| import android.view.View | import android.view.View | ||||
| import com.google.zxing.ResultPoint | import com.google.zxing.ResultPoint | ||||
| import android.hardware.Camera.CameraInfo | import android.hardware.Camera.CameraInfo | ||||
| import android.os.Build | import android.os.Build | ||||
| import com.google.zxing.BarcodeFormat | |||||
| import com.journeyapps.barcodescanner.BarcodeCallback | import com.journeyapps.barcodescanner.BarcodeCallback | ||||
| import com.journeyapps.barcodescanner.BarcodeResult | import com.journeyapps.barcodescanner.BarcodeResult | ||||
| import com.journeyapps.barcodescanner.BarcodeView | import com.journeyapps.barcodescanner.BarcodeView | ||||
| @@ -17,12 +17,11 @@ import io.flutter.plugin.common.BinaryMessenger | |||||
| import io.flutter.plugin.common.MethodCall | import io.flutter.plugin.common.MethodCall | ||||
| import io.flutter.plugin.common.MethodChannel | import io.flutter.plugin.common.MethodChannel | ||||
| import io.flutter.plugin.platform.PlatformView | import io.flutter.plugin.platform.PlatformView | ||||
| class QRView(messenger: BinaryMessenger, id: Int, private val context: Context) : | |||||
| class QRView(messenger: BinaryMessenger, id: Int, private val params: HashMap<String, Any>) : | |||||
| PlatformView, MethodChannel.MethodCallHandler { | PlatformView, MethodChannel.MethodCallHandler { | ||||
| private var isTorchOn: Boolean = false | private var isTorchOn: Boolean = false | ||||
| private var barcodeView: BarcodeView? = null | private var barcodeView: BarcodeView? = null | ||||
| private var requestingPermission = false | |||||
| private val channel: MethodChannel | private val channel: MethodChannel | ||||
| init { | init { | ||||
| @@ -65,89 +64,173 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context) | |||||
| } | } | ||||
| override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { | override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { | ||||
| when(call.method){ | |||||
| "flipCamera" -> { | |||||
| flipCamera() | |||||
| } | |||||
| "toggleFlash" -> { | |||||
| toggleFlash() | |||||
| } | |||||
| "pauseCamera" -> { | |||||
| pauseCamera() | |||||
| } | |||||
| "resumeCamera" -> { | |||||
| resumeCamera() | |||||
| } | |||||
| when(call.method) { | |||||
| "startScan" -> startScan(call.arguments as? List<Int>, result) | |||||
| "stopScan" -> stopScan() | |||||
| "flipCamera" -> flipCamera(result) | |||||
| "toggleFlash" -> toggleFlash(result) | |||||
| "pauseCamera" -> pauseCamera(result) | |||||
| "resumeCamera" -> resumeCamera(result) | |||||
| "requestPermissions" -> checkAndRequestPermission(result) | |||||
| "getCameraInfo" -> getCameraInfo(result) | |||||
| "getFlashInfo" -> getFlashInfo(result) | |||||
| "getSystemFeatures" -> getSystemFeatures(result) | |||||
| else -> result.notImplemented() | |||||
| } | } | ||||
| } | } | ||||
| private fun flipCamera() { | |||||
| barcodeView?.pause() | |||||
| val settings = barcodeView?.cameraSettings | |||||
| private fun getCameraInfo(result: MethodChannel.Result) { | |||||
| if (barcodeView == null) { | |||||
| return barCodeViewNotSet(result) | |||||
| } | |||||
| result.success(barcodeView!!.cameraSettings.requestedCameraId) | |||||
| } | |||||
| private fun flipCamera(result: MethodChannel.Result) { | |||||
| if (barcodeView == null) { | |||||
| return barCodeViewNotSet(result) | |||||
| } | |||||
| barcodeView!!.pause() | |||||
| val settings = barcodeView!!.cameraSettings | |||||
| if(settings?.requestedCameraId == CameraInfo.CAMERA_FACING_FRONT) | |||||
| if(settings.requestedCameraId == CameraInfo.CAMERA_FACING_FRONT) | |||||
| settings.requestedCameraId = CameraInfo.CAMERA_FACING_BACK | settings.requestedCameraId = CameraInfo.CAMERA_FACING_BACK | ||||
| else | else | ||||
| settings?.requestedCameraId = CameraInfo.CAMERA_FACING_FRONT | |||||
| settings.requestedCameraId = CameraInfo.CAMERA_FACING_FRONT | |||||
| barcodeView?.cameraSettings = settings | |||||
| barcodeView?.resume() | |||||
| barcodeView!!.cameraSettings = settings | |||||
| barcodeView!!.resume() | |||||
| result.success(settings.requestedCameraId) | |||||
| } | } | ||||
| private fun toggleFlash() { | |||||
| private fun getFlashInfo(result: MethodChannel.Result) { | |||||
| if (barcodeView == null) { | |||||
| return barCodeViewNotSet(result) | |||||
| } | |||||
| result.success(isTorchOn) | |||||
| } | |||||
| private fun toggleFlash(result: MethodChannel.Result) { | |||||
| if (barcodeView == null) { | |||||
| return barCodeViewNotSet(result) | |||||
| } | |||||
| if (hasFlash()) { | if (hasFlash()) { | ||||
| barcodeView?.setTorch(!isTorchOn) | |||||
| barcodeView!!.setTorch(!isTorchOn) | |||||
| isTorchOn = !isTorchOn | isTorchOn = !isTorchOn | ||||
| result.success(isTorchOn) | |||||
| } else { | |||||
| result.error("404", "This device doesn't support flash", null) | |||||
| } | } | ||||
| } | } | ||||
| private fun pauseCamera() { | |||||
| private fun pauseCamera(result: MethodChannel.Result) { | |||||
| if (barcodeView == null) { | |||||
| return barCodeViewNotSet(result) | |||||
| } | |||||
| if (barcodeView!!.isPreviewActive) { | if (barcodeView!!.isPreviewActive) { | ||||
| barcodeView?.pause() | |||||
| barcodeView!!.pause() | |||||
| } | } | ||||
| result.success(true) | |||||
| } | } | ||||
| private fun resumeCamera() { | |||||
| private fun resumeCamera(result: MethodChannel.Result) { | |||||
| if (barcodeView == null) { | |||||
| return barCodeViewNotSet(result) | |||||
| } | |||||
| if (!barcodeView!!.isPreviewActive) { | if (!barcodeView!!.isPreviewActive) { | ||||
| barcodeView?.resume() | |||||
| barcodeView!!.resume() | |||||
| } | } | ||||
| result.success(true) | |||||
| } | } | ||||
| private fun hasFlash(): Boolean { | private fun hasFlash(): Boolean { | ||||
| return context.packageManager | |||||
| .hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH) | |||||
| return hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH) | |||||
| } | |||||
| private fun hasBackCamera(): Boolean { | |||||
| return hasSystemFeature(PackageManager.FEATURE_CAMERA) | |||||
| } | |||||
| private fun hasFrontCamera(): Boolean { | |||||
| return hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT) | |||||
| } | |||||
| private fun hasSystemFeature(feature: String): Boolean { | |||||
| return Shared.activity!!.packageManager | |||||
| .hasSystemFeature(feature) | |||||
| } | |||||
| private fun barCodeViewNotSet(result: MethodChannel.Result) { | |||||
| result.error("404", "No barcode view found", null) | |||||
| } | } | ||||
| override fun getView(): View { | override fun getView(): View { | ||||
| return initBarCodeView()?.apply { | return initBarCodeView()?.apply { | ||||
| if (!hasBackCamera()) { | |||||
| if (!hasFrontCamera()) { | |||||
| // No camera available! | |||||
| } else { | |||||
| this.cameraSettings.requestedCameraId = CameraInfo.CAMERA_FACING_FRONT | |||||
| } | |||||
| } else { | |||||
| this.cameraSettings.requestedCameraId = CameraInfo.CAMERA_FACING_BACK | |||||
| } | |||||
| resume() | resume() | ||||
| }!! | }!! | ||||
| } | } | ||||
| private fun initBarCodeView(): BarcodeView? { | private fun initBarCodeView(): BarcodeView? { | ||||
| if (barcodeView == null) { | if (barcodeView == null) { | ||||
| barcodeView = createBarCodeView() | |||||
| barcodeView = BarcodeView(Shared.activity) | |||||
| if (params["cameraFacing"] as Int == 1) { | |||||
| barcodeView?.cameraSettings?.requestedCameraId = CameraInfo.CAMERA_FACING_FRONT | |||||
| } | |||||
| } | } | ||||
| return barcodeView | return barcodeView | ||||
| } | } | ||||
| private fun createBarCodeView(): BarcodeView { | |||||
| val barcode = BarcodeView(Shared.activity) | |||||
| barcode.decodeContinuous( | |||||
| private fun startScan(arguments: List<Int>?, result: MethodChannel.Result) { | |||||
| val allowedBarcodeTypes = mutableListOf<BarcodeFormat>() | |||||
| try { | |||||
| arguments?.forEach { | |||||
| allowedBarcodeTypes.add(BarcodeFormat.values()[it]) | |||||
| } | |||||
| } catch (e: java.lang.Exception) { | |||||
| result.error(null, null, null) | |||||
| } | |||||
| barcodeView?.decodeContinuous( | |||||
| object : BarcodeCallback { | object : BarcodeCallback { | ||||
| override fun barcodeResult(result: BarcodeResult) { | override fun barcodeResult(result: BarcodeResult) { | ||||
| val code = mapOf( | |||||
| "code" to result.text, | |||||
| "type" to result.barcodeFormat.name, | |||||
| "rawBytes" to result.rawBytes) | |||||
| channel.invokeMethod("onRecognizeQR", code) | |||||
| if (allowedBarcodeTypes.size == 0 || allowedBarcodeTypes.contains(result.barcodeFormat)) { | |||||
| val code = mapOf( | |||||
| "code" to result.text, | |||||
| "type" to result.barcodeFormat.name, | |||||
| "rawBytes" to result.rawBytes) | |||||
| channel.invokeMethod("onRecognizeQR", code) | |||||
| } | |||||
| } | } | ||||
| override fun possibleResultPoints(resultPoints: List<ResultPoint>) {} | override fun possibleResultPoints(resultPoints: List<ResultPoint>) {} | ||||
| } | } | ||||
| ) | ) | ||||
| return barcode | |||||
| } | |||||
| private fun stopScan() { | |||||
| barcodeView?.stopDecoding() | |||||
| } | |||||
| private fun getSystemFeatures(result: MethodChannel.Result) { | |||||
| try { | |||||
| result.success(mapOf("hasFrontCamera" to hasFrontCamera(), | |||||
| "hasBackCamera" to hasBackCamera(), "hasFlash" to hasFlash(), | |||||
| "activeCamera" to barcodeView?.cameraSettings?.requestedCameraId)) | |||||
| } catch (e: Exception) { | |||||
| result.error(null, null, null) | |||||
| } | |||||
| } | } | ||||
| private fun hasCameraPermission(): Boolean { | private fun hasCameraPermission(): Boolean { | ||||
| @@ -156,29 +239,16 @@ class QRView(messenger: BinaryMessenger, id: Int, private val context: Context) | |||||
| } | } | ||||
| private fun checkAndRequestPermission(result: MethodChannel.Result?) { | private fun checkAndRequestPermission(result: MethodChannel.Result?) { | ||||
| if (Shared.cameraPermissionContinuation != null) { | |||||
| result?.error("cameraPermission", "Camera permission request ongoing", null) | |||||
| } | |||||
| Shared.cameraPermissionContinuation = Runnable { | |||||
| Shared.cameraPermissionContinuation = null | |||||
| if (!hasCameraPermission()) { | |||||
| result?.error( | |||||
| "cameraPermission", "MediaRecorderCamera permission not granted", null) | |||||
| return@Runnable | |||||
| } | |||||
| } | |||||
| requestingPermission = false | |||||
| if (hasCameraPermission()) { | |||||
| Shared.cameraPermissionContinuation?.run() | |||||
| } else { | |||||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | |||||
| requestingPermission = true | |||||
| when { | |||||
| hasCameraPermission() -> result?.success(true) | |||||
| Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> { | |||||
| Shared.activity?.requestPermissions( | Shared.activity?.requestPermissions( | ||||
| arrayOf(Manifest.permission.CAMERA), | arrayOf(Manifest.permission.CAMERA), | ||||
| Shared.CAMERA_REQUEST_ID) | Shared.CAMERA_REQUEST_ID) | ||||
| } | } | ||||
| else -> { | |||||
| result?.error("cameraPermission", "Platform Version to low for camera permission check", null) | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -10,8 +10,9 @@ import io.flutter.plugin.platform.PlatformViewFactory | |||||
| class QRViewFactory(private val messenger: BinaryMessenger) : | class QRViewFactory(private val messenger: BinaryMessenger) : | ||||
| PlatformViewFactory(StandardMessageCodec.INSTANCE) { | PlatformViewFactory(StandardMessageCodec.INSTANCE) { | ||||
| override fun create(context: Context, id: Int, obj: Any?): PlatformView { | |||||
| return QRView(messenger, id, context) | |||||
| override fun create(context: Context, id: Int, args: Any?): PlatformView { | |||||
| val params = args as HashMap<String, Any> | |||||
| return QRView(messenger, id, params) | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,5 +5,4 @@ import android.app.Activity | |||||
| object Shared { | object Shared { | ||||
| const val CAMERA_REQUEST_ID = 513469796 | const val CAMERA_REQUEST_ID = 513469796 | ||||
| var activity: Activity? = null | var activity: Activity? = null | ||||
| var cameraPermissionContinuation: Runnable? = null | |||||
| } | } | ||||
| @@ -9,6 +9,7 @@ | |||||
| .buildlog/ | .buildlog/ | ||||
| .history | .history | ||||
| .svn/ | .svn/ | ||||
| .last_build_id | |||||
| # IntelliJ related | # IntelliJ related | ||||
| *.iml | *.iml | ||||
| @@ -9,11 +9,11 @@ | |||||
| /* Begin PBXBuildFile section */ | /* Begin PBXBuildFile section */ | ||||
| 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; | ||||
| 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; | ||||
| 4FA3805501F593094264678B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75B64521967C76C845268E71 /* Pods_Runner.framework */; }; | |||||
| 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; | ||||
| 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; | ||||
| 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; | ||||
| 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; | ||||
| F50BD2AA150CE43D961DBB69 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C211BF4383BB3311CB0313D6 /* Pods_Runner.framework */; }; | |||||
| /* End PBXBuildFile section */ | /* End PBXBuildFile section */ | ||||
| /* Begin PBXCopyFilesBuildPhase section */ | /* Begin PBXCopyFilesBuildPhase section */ | ||||
| @@ -30,15 +30,14 @@ | |||||
| /* End PBXCopyFilesBuildPhase section */ | /* End PBXCopyFilesBuildPhase section */ | ||||
| /* Begin PBXFileReference section */ | /* Begin PBXFileReference section */ | ||||
| 11A772D5271EABFB7473B52C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | |||||
| 08F42BD28C1585A831B4FDA2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; | |||||
| 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; | ||||
| 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; | ||||
| 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; | ||||
| 4D86354DC939411D3F7E41EA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | |||||
| 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; | ||||
| 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | ||||
| 75B64521967C76C845268E71 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | |||||
| 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; | ||||
| 96DCE88542EFC8C39614159F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; | |||||
| 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; | ||||
| 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; | ||||
| 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | ||||
| @@ -46,7 +45,8 @@ | |||||
| 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; | ||||
| 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; | ||||
| 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | ||||
| C211BF4383BB3311CB0313D6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | |||||
| DB8991B7B046D94B2E68CEAD /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | |||||
| EF8A0739582463E296AD6CB1 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | |||||
| /* End PBXFileReference section */ | /* End PBXFileReference section */ | ||||
| /* Begin PBXFrameworksBuildPhase section */ | /* Begin PBXFrameworksBuildPhase section */ | ||||
| @@ -54,21 +54,13 @@ | |||||
| isa = PBXFrameworksBuildPhase; | isa = PBXFrameworksBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| F50BD2AA150CE43D961DBB69 /* Pods_Runner.framework in Frameworks */, | |||||
| 4FA3805501F593094264678B /* Pods_Runner.framework in Frameworks */, | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| }; | }; | ||||
| /* End PBXFrameworksBuildPhase section */ | /* End PBXFrameworksBuildPhase section */ | ||||
| /* Begin PBXGroup section */ | /* Begin PBXGroup section */ | ||||
| 509AC3E235E568B6DDAAEC7D /* Frameworks */ = { | |||||
| isa = PBXGroup; | |||||
| children = ( | |||||
| C211BF4383BB3311CB0313D6 /* Pods_Runner.framework */, | |||||
| ); | |||||
| name = Frameworks; | |||||
| sourceTree = "<group>"; | |||||
| }; | |||||
| 9740EEB11CF90186004384FC /* Flutter */ = { | 9740EEB11CF90186004384FC /* Flutter */ = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| @@ -87,7 +79,7 @@ | |||||
| 97C146F01CF9000F007C117D /* Runner */, | 97C146F01CF9000F007C117D /* Runner */, | ||||
| 97C146EF1CF9000F007C117D /* Products */, | 97C146EF1CF9000F007C117D /* Products */, | ||||
| D7C58E57CAF69D9A14CC212F /* Pods */, | D7C58E57CAF69D9A14CC212F /* Pods */, | ||||
| 509AC3E235E568B6DDAAEC7D /* Frameworks */, | |||||
| AF031A634C9742F16BF4F89A /* Frameworks */, | |||||
| ); | ); | ||||
| sourceTree = "<group>"; | sourceTree = "<group>"; | ||||
| }; | }; | ||||
| @@ -122,12 +114,20 @@ | |||||
| name = "Supporting Files"; | name = "Supporting Files"; | ||||
| sourceTree = "<group>"; | sourceTree = "<group>"; | ||||
| }; | }; | ||||
| AF031A634C9742F16BF4F89A /* Frameworks */ = { | |||||
| isa = PBXGroup; | |||||
| children = ( | |||||
| 75B64521967C76C845268E71 /* Pods_Runner.framework */, | |||||
| ); | |||||
| name = Frameworks; | |||||
| sourceTree = "<group>"; | |||||
| }; | |||||
| D7C58E57CAF69D9A14CC212F /* Pods */ = { | D7C58E57CAF69D9A14CC212F /* Pods */ = { | ||||
| isa = PBXGroup; | isa = PBXGroup; | ||||
| children = ( | children = ( | ||||
| 4D86354DC939411D3F7E41EA /* Pods-Runner.debug.xcconfig */, | |||||
| 11A772D5271EABFB7473B52C /* Pods-Runner.release.xcconfig */, | |||||
| 96DCE88542EFC8C39614159F /* Pods-Runner.profile.xcconfig */, | |||||
| DB8991B7B046D94B2E68CEAD /* Pods-Runner.debug.xcconfig */, | |||||
| EF8A0739582463E296AD6CB1 /* Pods-Runner.release.xcconfig */, | |||||
| 08F42BD28C1585A831B4FDA2 /* Pods-Runner.profile.xcconfig */, | |||||
| ); | ); | ||||
| path = Pods; | path = Pods; | ||||
| sourceTree = "<group>"; | sourceTree = "<group>"; | ||||
| @@ -139,14 +139,14 @@ | |||||
| isa = PBXNativeTarget; | isa = PBXNativeTarget; | ||||
| buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; | ||||
| buildPhases = ( | buildPhases = ( | ||||
| 854A11C1BE2493D5A6BD85DB /* [CP] Check Pods Manifest.lock */, | |||||
| 7BEA526A4A0DFAAC4945EC09 /* [CP] Check Pods Manifest.lock */, | |||||
| 9740EEB61CF901F6004384FC /* Run Script */, | 9740EEB61CF901F6004384FC /* Run Script */, | ||||
| 97C146EA1CF9000F007C117D /* Sources */, | 97C146EA1CF9000F007C117D /* Sources */, | ||||
| 97C146EB1CF9000F007C117D /* Frameworks */, | 97C146EB1CF9000F007C117D /* Frameworks */, | ||||
| 97C146EC1CF9000F007C117D /* Resources */, | 97C146EC1CF9000F007C117D /* Resources */, | ||||
| 9705A1C41CF9048500538489 /* Embed Frameworks */, | 9705A1C41CF9048500538489 /* Embed Frameworks */, | ||||
| 3B06AD1E1E4923F5004D2608 /* Thin Binary */, | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, | ||||
| FDE6A3C9913AC5840FF42544 /* [CP] Embed Pods Frameworks */, | |||||
| 8DB6578C615972DBF2DFAA0D /* [CP] Embed Pods Frameworks */, | |||||
| ); | ); | ||||
| buildRules = ( | buildRules = ( | ||||
| ); | ); | ||||
| @@ -168,7 +168,7 @@ | |||||
| TargetAttributes = { | TargetAttributes = { | ||||
| 97C146ED1CF9000F007C117D = { | 97C146ED1CF9000F007C117D = { | ||||
| CreatedOnToolsVersion = 7.3.1; | CreatedOnToolsVersion = 7.3.1; | ||||
| DevelopmentTeam = 5LSCTACHT8; | |||||
| DevelopmentTeam = RCH2VG82SH; | |||||
| LastSwiftMigration = 1020; | LastSwiftMigration = 1020; | ||||
| ProvisioningStyle = Automatic; | ProvisioningStyle = Automatic; | ||||
| }; | }; | ||||
| @@ -221,7 +221,7 @@ | |||||
| shellPath = /bin/sh; | shellPath = /bin/sh; | ||||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; | ||||
| }; | }; | ||||
| 854A11C1BE2493D5A6BD85DB /* [CP] Check Pods Manifest.lock */ = { | |||||
| 7BEA526A4A0DFAAC4945EC09 /* [CP] Check Pods Manifest.lock */ = { | |||||
| isa = PBXShellScriptBuildPhase; | isa = PBXShellScriptBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| @@ -243,41 +243,41 @@ | |||||
| shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | ||||
| showEnvVarsInLog = 0; | showEnvVarsInLog = 0; | ||||
| }; | }; | ||||
| 9740EEB61CF901F6004384FC /* Run Script */ = { | |||||
| 8DB6578C615972DBF2DFAA0D /* [CP] Embed Pods Frameworks */ = { | |||||
| isa = PBXShellScriptBuildPhase; | isa = PBXShellScriptBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| ); | ); | ||||
| inputPaths = ( | inputPaths = ( | ||||
| "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", | |||||
| "${PODS_ROOT}/../Flutter/Flutter.framework", | |||||
| "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", | |||||
| "${BUILT_PRODUCTS_DIR}/qr_code_scanner/qr_code_scanner.framework", | |||||
| ); | ); | ||||
| name = "Run Script"; | |||||
| name = "[CP] Embed Pods Frameworks"; | |||||
| outputPaths = ( | outputPaths = ( | ||||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", | |||||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", | |||||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/qr_code_scanner.framework", | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| shellPath = /bin/sh; | shellPath = /bin/sh; | ||||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; | |||||
| shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | |||||
| showEnvVarsInLog = 0; | |||||
| }; | }; | ||||
| FDE6A3C9913AC5840FF42544 /* [CP] Embed Pods Frameworks */ = { | |||||
| 9740EEB61CF901F6004384FC /* Run Script */ = { | |||||
| isa = PBXShellScriptBuildPhase; | isa = PBXShellScriptBuildPhase; | ||||
| buildActionMask = 2147483647; | buildActionMask = 2147483647; | ||||
| files = ( | files = ( | ||||
| ); | ); | ||||
| inputPaths = ( | inputPaths = ( | ||||
| "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", | |||||
| "${PODS_ROOT}/../Flutter/Flutter.framework", | |||||
| "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", | |||||
| "${BUILT_PRODUCTS_DIR}/qr_code_scanner/qr_code_scanner.framework", | |||||
| ); | ); | ||||
| name = "[CP] Embed Pods Frameworks"; | |||||
| name = "Run Script"; | |||||
| outputPaths = ( | outputPaths = ( | ||||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", | |||||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", | |||||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/qr_code_scanner.framework", | |||||
| ); | ); | ||||
| runOnlyForDeploymentPostprocessing = 0; | runOnlyForDeploymentPostprocessing = 0; | ||||
| shellPath = /bin/sh; | shellPath = /bin/sh; | ||||
| shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | |||||
| showEnvVarsInLog = 0; | |||||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; | |||||
| }; | }; | ||||
| /* End PBXShellScriptBuildPhase section */ | /* End PBXShellScriptBuildPhase section */ | ||||
| @@ -372,7 +372,7 @@ | |||||
| CODE_SIGN_IDENTITY = "iPhone Developer"; | CODE_SIGN_IDENTITY = "iPhone Developer"; | ||||
| CODE_SIGN_STYLE = Automatic; | CODE_SIGN_STYLE = Automatic; | ||||
| CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | ||||
| DEVELOPMENT_TEAM = 5LSCTACHT8; | |||||
| DEVELOPMENT_TEAM = RCH2VG82SH; | |||||
| ENABLE_BITCODE = NO; | ENABLE_BITCODE = NO; | ||||
| FRAMEWORK_SEARCH_PATHS = ( | FRAMEWORK_SEARCH_PATHS = ( | ||||
| "$(inherited)", | "$(inherited)", | ||||
| @@ -510,7 +510,7 @@ | |||||
| CODE_SIGN_IDENTITY = "iPhone Developer"; | CODE_SIGN_IDENTITY = "iPhone Developer"; | ||||
| CODE_SIGN_STYLE = Automatic; | CODE_SIGN_STYLE = Automatic; | ||||
| CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | ||||
| DEVELOPMENT_TEAM = 5LSCTACHT8; | |||||
| DEVELOPMENT_TEAM = RCH2VG82SH; | |||||
| ENABLE_BITCODE = NO; | ENABLE_BITCODE = NO; | ||||
| FRAMEWORK_SEARCH_PATHS = ( | FRAMEWORK_SEARCH_PATHS = ( | ||||
| "$(inherited)", | "$(inherited)", | ||||
| @@ -541,7 +541,7 @@ | |||||
| CODE_SIGN_IDENTITY = "iPhone Developer"; | CODE_SIGN_IDENTITY = "iPhone Developer"; | ||||
| CODE_SIGN_STYLE = Automatic; | CODE_SIGN_STYLE = Automatic; | ||||
| CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | ||||
| DEVELOPMENT_TEAM = 5LSCTACHT8; | |||||
| DEVELOPMENT_TEAM = RCH2VG82SH; | |||||
| ENABLE_BITCODE = NO; | ENABLE_BITCODE = NO; | ||||
| FRAMEWORK_SEARCH_PATHS = ( | FRAMEWORK_SEARCH_PATHS = ( | ||||
| "$(inherited)", | "$(inherited)", | ||||
| @@ -6,11 +6,6 @@ import 'package:qr_code_scanner/qr_code_scanner.dart'; | |||||
| void main() => runApp(MaterialApp(home: QRViewExample())); | void main() => runApp(MaterialApp(home: QRViewExample())); | ||||
| const flashOn = 'FLASH ON'; | |||||
| const flashOff = 'FLASH OFF'; | |||||
| const frontCamera = 'FRONT CAMERA'; | |||||
| const backCamera = 'BACK CAMERA'; | |||||
| class QRViewExample extends StatefulWidget { | class QRViewExample extends StatefulWidget { | ||||
| const QRViewExample({ | const QRViewExample({ | ||||
| Key key, | Key key, | ||||
| @@ -22,8 +17,6 @@ class QRViewExample extends StatefulWidget { | |||||
| class _QRViewExampleState extends State<QRViewExample> { | class _QRViewExampleState extends State<QRViewExample> { | ||||
| Barcode result; | Barcode result; | ||||
| var flashState = flashOn; | |||||
| var cameraState = frontCamera; | |||||
| QRViewController controller; | QRViewController controller; | ||||
| final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); | final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); | ||||
| @@ -64,44 +57,33 @@ class _QRViewExampleState extends State<QRViewExample> { | |||||
| Container( | Container( | ||||
| margin: EdgeInsets.all(8), | margin: EdgeInsets.all(8), | ||||
| child: RaisedButton( | child: RaisedButton( | ||||
| onPressed: () { | |||||
| if (controller != null) { | |||||
| controller.toggleFlash(); | |||||
| if (_isFlashOn(flashState)) { | |||||
| setState(() { | |||||
| flashState = flashOff; | |||||
| }); | |||||
| } else { | |||||
| setState(() { | |||||
| flashState = flashOn; | |||||
| }); | |||||
| } | |||||
| } | |||||
| }, | |||||
| child: | |||||
| Text(flashState, style: TextStyle(fontSize: 20)), | |||||
| ), | |||||
| onPressed: () => setState(() { | |||||
| controller?.toggleFlash(); | |||||
| }), | |||||
| child: FutureBuilder( | |||||
| future: controller?.getFlashStatus(), | |||||
| builder: (context, snapshot) { | |||||
| return Text('Flash: ${snapshot.data}'); | |||||
| }, | |||||
| )), | |||||
| ), | ), | ||||
| Container( | Container( | ||||
| margin: EdgeInsets.all(8), | margin: EdgeInsets.all(8), | ||||
| child: RaisedButton( | child: RaisedButton( | ||||
| onPressed: () { | |||||
| if (controller != null) { | |||||
| controller.flipCamera(); | |||||
| if (_isBackCamera(cameraState)) { | |||||
| setState(() { | |||||
| cameraState = frontCamera; | |||||
| }); | |||||
| } else { | |||||
| setState(() { | |||||
| cameraState = backCamera; | |||||
| }); | |||||
| } | |||||
| } | |||||
| }, | |||||
| child: | |||||
| Text(cameraState, style: TextStyle(fontSize: 20)), | |||||
| ), | |||||
| onPressed: () => setState(() { | |||||
| controller?.flipCamera(); | |||||
| }), | |||||
| child: FutureBuilder( | |||||
| future: controller?.getCameraInfo(), | |||||
| builder: (context, snapshot) { | |||||
| if (snapshot.data != null) { | |||||
| return Text( | |||||
| 'Camera facing ${describeEnum(snapshot.data)}'); | |||||
| } else { | |||||
| return Text('loading'); | |||||
| } | |||||
| }, | |||||
| )), | |||||
| ) | ) | ||||
| ], | ], | ||||
| ), | ), | ||||
| @@ -138,14 +120,6 @@ class _QRViewExampleState extends State<QRViewExample> { | |||||
| ); | ); | ||||
| } | } | ||||
| bool _isFlashOn(String current) { | |||||
| return flashOn == current; | |||||
| } | |||||
| bool _isBackCamera(String current) { | |||||
| return backCamera == current; | |||||
| } | |||||
| Widget _buildQrView(BuildContext context) { | Widget _buildQrView(BuildContext context) { | ||||
| // For this example we check how width or tall the device is and change the scanArea and overlay accordingly. | // For this example we check how width or tall the device is and change the scanArea and overlay accordingly. | ||||
| var scanArea = (MediaQuery.of(context).size.width < 400 || | var scanArea = (MediaQuery.of(context).size.width < 400 || | ||||
| @@ -154,29 +128,25 @@ class _QRViewExampleState extends State<QRViewExample> { | |||||
| : 300.0; | : 300.0; | ||||
| // To ensure the Scanner view is properly sizes after rotation | // To ensure the Scanner view is properly sizes after rotation | ||||
| // we need to listen for Flutter SizeChanged notification and update controller | // we need to listen for Flutter SizeChanged notification and update controller | ||||
| return NotificationListener<SizeChangedLayoutNotification>( | |||||
| onNotification: (notification) { | |||||
| Future.microtask( | |||||
| () => controller?.updateDimensions(qrKey, scanArea: scanArea)); | |||||
| return false; | |||||
| }, | |||||
| child: SizeChangedLayoutNotifier( | |||||
| key: const Key('qr-size-notifier'), | |||||
| child: QRView( | |||||
| key: qrKey, | |||||
| onQRViewCreated: _onQRViewCreated, | |||||
| overlay: QrScannerOverlayShape( | |||||
| borderColor: Colors.red, | |||||
| borderRadius: 10, | |||||
| borderLength: 30, | |||||
| borderWidth: 10, | |||||
| cutOutSize: scanArea, | |||||
| ), | |||||
| ))); | |||||
| return QRView( | |||||
| key: qrKey, | |||||
| cameraFacing: CameraFacing.front, | |||||
| onQRViewCreated: _onQRViewCreated, | |||||
| formatsAllowed: [BarcodeFormat.qrcode], | |||||
| overlay: QrScannerOverlayShape( | |||||
| borderColor: Colors.red, | |||||
| borderRadius: 10, | |||||
| borderLength: 30, | |||||
| borderWidth: 10, | |||||
| cutOutSize: scanArea, | |||||
| ), | |||||
| ); | |||||
| } | } | ||||
| void _onQRViewCreated(QRViewController controller) { | void _onQRViewCreated(QRViewController controller) { | ||||
| this.controller = controller; | |||||
| setState(() { | |||||
| this.controller = controller; | |||||
| }); | |||||
| controller.scannedDataStream.listen((scanData) { | controller.scannedDataStream.listen((scanData) { | ||||
| setState(() { | setState(() { | ||||
| result = scanData; | result = scanData; | ||||
| @@ -3,7 +3,7 @@ description: Demonstrates how to use the flutter_qr plugin. | |||||
| publish_to: 'none' | publish_to: 'none' | ||||
| environment: | environment: | ||||
| sdk: ">=2.3.0 <3.0.0" | |||||
| sdk: ">=2.6.0 <3.0.0" | |||||
| dependencies: | dependencies: | ||||
| flutter: | flutter: | ||||
| @@ -13,75 +13,56 @@ public class QRView:NSObject,FlutterPlatformView { | |||||
| var scanner: MTBBarcodeScanner? | var scanner: MTBBarcodeScanner? | ||||
| var registrar: FlutterPluginRegistrar | var registrar: FlutterPluginRegistrar | ||||
| var channel: FlutterMethodChannel | var channel: FlutterMethodChannel | ||||
| var cameraFacing: MTBCamera | |||||
| public init(withFrame frame: CGRect, withRegistrar registrar: FlutterPluginRegistrar, withId id: Int64){ | |||||
| var QRCodeTypes = [ | |||||
| 0: AVMetadataObject.ObjectType.aztec, | |||||
| 2: AVMetadataObject.ObjectType.code39, | |||||
| 3: AVMetadataObject.ObjectType.code93, | |||||
| 4: AVMetadataObject.ObjectType.code128, | |||||
| 5: AVMetadataObject.ObjectType.dataMatrix, | |||||
| 6: AVMetadataObject.ObjectType.ean8, | |||||
| 7: AVMetadataObject.ObjectType.ean13, | |||||
| 8: AVMetadataObject.ObjectType.interleaved2of5, | |||||
| 10: AVMetadataObject.ObjectType.pdf417, | |||||
| 11: AVMetadataObject.ObjectType.qr, | |||||
| 15: AVMetadataObject.ObjectType.upce | |||||
| ] | |||||
| public init(withFrame frame: CGRect, withRegistrar registrar: FlutterPluginRegistrar, withId id: Int64, params: Dictionary<String, Any>){ | |||||
| self.registrar = registrar | self.registrar = registrar | ||||
| previewView = UIView(frame: frame) | previewView = UIView(frame: frame) | ||||
| cameraFacing = MTBCamera.init(rawValue: UInt(Int(params["cameraFacing"] as! Double))) ?? MTBCamera.back | |||||
| channel = FlutterMethodChannel(name: "net.touchcapture.qr.flutterqr/qrview_\(id)", binaryMessenger: registrar.messenger()) | channel = FlutterMethodChannel(name: "net.touchcapture.qr.flutterqr/qrview_\(id)", binaryMessenger: registrar.messenger()) | ||||
| } | } | ||||
| func isCameraAvailable(success: Bool) -> Void { | |||||
| if success { | |||||
| do { | |||||
| 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 } | |||||
| let result = ["code": stringValue, "type": typeString] | |||||
| self?.channel.invokeMethod("onRecognizeQR", arguments: result) | |||||
| } | |||||
| } | |||||
| }) | |||||
| } catch { | |||||
| NSLog("Unable to start scanning") | |||||
| } | |||||
| } else { | |||||
| UIAlertView(title: "Scanning Unavailable", message: "This app does not have permission to access the camera", delegate: nil, cancelButtonTitle: nil, otherButtonTitles: "Ok").show() | |||||
| } | |||||
| deinit { | |||||
| scanner?.stopScanning() | |||||
| } | } | ||||
| public func view() -> UIView { | public func view() -> UIView { | ||||
| channel.setMethodCallHandler({ | channel.setMethodCallHandler({ | ||||
| [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in | |||||
| [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in | |||||
| switch(call.method){ | switch(call.method){ | ||||
| case "setDimensions": | case "setDimensions": | ||||
| let arguments = call.arguments as! Dictionary<String, Double> | let arguments = call.arguments as! Dictionary<String, Double> | ||||
| self?.setDimensions(width: arguments["width"] ?? 0, height: arguments["height"] ?? 0, scanArea: arguments["scanArea"] ?? 0) | self?.setDimensions(width: arguments["width"] ?? 0, height: arguments["height"] ?? 0, scanArea: arguments["scanArea"] ?? 0) | ||||
| case "startScan": | |||||
| self?.startScan(call.arguments as! Array<Int>, result) | |||||
| case "flipCamera": | case "flipCamera": | ||||
| self?.flipCamera() | |||||
| self?.flipCamera(result) | |||||
| case "toggleFlash": | case "toggleFlash": | ||||
| self?.toggleFlash() | |||||
| self?.toggleFlash(result) | |||||
| case "pauseCamera": | case "pauseCamera": | ||||
| self?.pauseCamera() | |||||
| self?.pauseCamera(result) | |||||
| case "resumeCamera": | case "resumeCamera": | ||||
| self?.resumeCamera() | |||||
| self?.resumeCamera(result) | |||||
| case "getCameraInfo": | |||||
| self?.getCameraInfo(result) | |||||
| case "getFlashInfo": | |||||
| self?.getFlashInfo(result) | |||||
| case "getSystemFeatures": | |||||
| self?.getSystemFeatures(result) | |||||
| default: | default: | ||||
| result(FlutterMethodNotImplemented) | result(FlutterMethodNotImplemented) | ||||
| return | return | ||||
| @@ -106,41 +87,162 @@ public class QRView:NSObject,FlutterPlatformView { | |||||
| self.scanner?.scanRect = CGRect(x: Double(midX) - (scanArea / 2), y: Double(midY) - (scanArea / 2), width: scanArea, height: scanArea) | self.scanner?.scanRect = CGRect(x: Double(midX) - (scanArea / 2), y: Double(midY) - (scanArea / 2), width: scanArea, height: scanArea) | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| } | |||||
| func startScan(_ arguments: Array<Int>, _ result: @escaping FlutterResult) -> Void { | |||||
| scanner = MTBBarcodeScanner(previewView: previewView) | |||||
| var allowedBarcodeTypes: Array<AVMetadataObject.ObjectType> = [] | |||||
| arguments.forEach { arg in | |||||
| allowedBarcodeTypes.append( QRCodeTypes[arg]!) | |||||
| } | |||||
| MTBBarcodeScanner.requestCameraPermission(success: isCameraAvailable) | |||||
| MTBBarcodeScanner.requestCameraPermission(success: { permissionGranted in | |||||
| if permissionGranted { | |||||
| do { | |||||
| try self.scanner?.startScanning(with: self.cameraFacing, 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 } | |||||
| let result = ["code": stringValue, "type": typeString] | |||||
| if allowedBarcodeTypes.count == 0 || allowedBarcodeTypes.contains(code.type) { | |||||
| self?.channel.invokeMethod("onRecognizeQR", arguments: result) | |||||
| } | |||||
| } | |||||
| } | |||||
| }) | |||||
| } catch { | |||||
| let error = FlutterError(code: "unknown-error", message: "Unable to start scanning", details: nil) | |||||
| result(error) | |||||
| } | |||||
| } else { | |||||
| let error = FlutterError(code: "cameraPermission", message: "Permission denied to access the camera", details: nil) | |||||
| result(error) | |||||
| } | |||||
| }) | |||||
| } | |||||
| func stopScan(){ | |||||
| if let sc: MTBBarcodeScanner = scanner { | |||||
| if sc.isScanning() { | |||||
| sc.stopScanning() | |||||
| } | |||||
| } | |||||
| } | |||||
| func getCameraInfo(_ result: @escaping FlutterResult) -> Void { | |||||
| if let sc: MTBBarcodeScanner = scanner { | |||||
| result(sc.camera.rawValue) | |||||
| } else { | |||||
| let error = FlutterError(code: "cameraInformationError", message: "Could not get camera information", details: nil) | |||||
| result(error) | |||||
| } | } | ||||
| } | } | ||||
| func flipCamera(){ | |||||
| func flipCamera(_ result: @escaping FlutterResult){ | |||||
| if let sc: MTBBarcodeScanner = scanner { | if let sc: MTBBarcodeScanner = scanner { | ||||
| if sc.hasOppositeCamera() { | if sc.hasOppositeCamera() { | ||||
| sc.flipCamera() | sc.flipCamera() | ||||
| } | } | ||||
| return result(sc.camera.rawValue) | |||||
| } | |||||
| return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) | |||||
| } | |||||
| func getFlashInfo(_ result: @escaping FlutterResult) -> Void { | |||||
| if let sc: MTBBarcodeScanner = scanner { | |||||
| result(sc.torchMode.rawValue != 0) | |||||
| } else { | |||||
| let error = FlutterError(code: "cameraInformationError", message: "Could not get flash information", details: nil) | |||||
| result(error) | |||||
| } | } | ||||
| } | } | ||||
| func toggleFlash(){ | |||||
| func toggleFlash(_ result: @escaping FlutterResult){ | |||||
| if let sc: MTBBarcodeScanner = scanner { | if let sc: MTBBarcodeScanner = scanner { | ||||
| if sc.hasTorch() { | if sc.hasTorch() { | ||||
| sc.toggleTorch() | sc.toggleTorch() | ||||
| return result(sc.torchMode == MTBTorchMode(rawValue: 1)) | |||||
| } | } | ||||
| return result(FlutterError(code: "404", message: "This device doesn\'t support flash", details: nil)) | |||||
| } | } | ||||
| return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) | |||||
| } | } | ||||
| func pauseCamera() { | |||||
| func pauseCamera(_ result: @escaping FlutterResult) { | |||||
| if let sc: MTBBarcodeScanner = scanner { | if let sc: MTBBarcodeScanner = scanner { | ||||
| if sc.isScanning() { | if sc.isScanning() { | ||||
| sc.freezeCapture() | sc.freezeCapture() | ||||
| } | } | ||||
| return result(true) | |||||
| } | } | ||||
| return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) | |||||
| } | } | ||||
| func resumeCamera() { | |||||
| func resumeCamera(_ result: @escaping FlutterResult) { | |||||
| if let sc: MTBBarcodeScanner = scanner { | if let sc: MTBBarcodeScanner = scanner { | ||||
| if !sc.isScanning() { | if !sc.isScanning() { | ||||
| sc.unfreezeCapture() | sc.unfreezeCapture() | ||||
| } | } | ||||
| return result(true) | |||||
| } | |||||
| return result(FlutterError(code: "404", message: "No barcode scanner found", details: nil)) | |||||
| } | |||||
| func getSystemFeatures(_ result: @escaping FlutterResult) -> Void { | |||||
| if let sc: MTBBarcodeScanner = scanner { | |||||
| var hasBackCameraVar = false | |||||
| var hasFrontCameraVar = false | |||||
| let camera = sc.camera | |||||
| if(camera == MTBCamera(rawValue: 0)){ | |||||
| hasBackCameraVar = true | |||||
| if sc.hasOppositeCamera() { | |||||
| hasFrontCameraVar = true | |||||
| } | |||||
| }else{ | |||||
| hasFrontCameraVar = true | |||||
| if sc.hasOppositeCamera() { | |||||
| hasBackCameraVar = true | |||||
| } | |||||
| } | |||||
| return result([ | |||||
| "hasFrontCamera": hasFrontCameraVar, | |||||
| "hasBackCamera": hasBackCameraVar, | |||||
| "hasFlash": sc.hasTorch(), | |||||
| "activeCamera": camera.rawValue | |||||
| ]) | |||||
| } | } | ||||
| return result(FlutterError(code: "404", message: nil, details: nil)) | |||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -17,8 +17,8 @@ public class QRViewFactory: NSObject, FlutterPlatformViewFactory { | |||||
| } | } | ||||
| public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { | public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { | ||||
| let dictionary = args as! Dictionary<String, Double> | |||||
| return QRView(withFrame: CGRect(x: 0, y: 0, width: dictionary["width"] ?? 0, height: dictionary["height"] ?? 0), withRegistrar: registrar!,withId: viewId) | |||||
| let params = args as! Dictionary<String, Double> | |||||
| return QRView(withFrame: frame, withRegistrar: registrar!,withId: viewId, params: params) | |||||
| } | } | ||||
| public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { | public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { | ||||
| @@ -1,2 +1,7 @@ | |||||
| export 'src/qr_code_scanner.dart'; | export 'src/qr_code_scanner.dart'; | ||||
| export 'src/qr_scanner_overlay_shape.dart'; | export 'src/qr_scanner_overlay_shape.dart'; | ||||
| export 'src/types/barcode.dart'; | |||||
| export 'src/types/barcode_format.dart'; | |||||
| export 'src/types/camera.dart'; | |||||
| export 'src/types/camera_exception.dart'; | |||||
| export 'src/types/features.dart'; | |||||
| @@ -3,138 +3,107 @@ import 'dart:async'; | |||||
| import 'package:flutter/foundation.dart'; | import 'package:flutter/foundation.dart'; | ||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | import 'package:flutter/services.dart'; | ||||
| import 'package:qr_code_scanner/qr_code_scanner.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, | |||||
| import 'qr_scanner_overlay_shape.dart'; | |||||
| import 'types/barcode.dart'; | |||||
| import 'types/barcode_format.dart'; | |||||
| import 'types/camera.dart'; | |||||
| import 'types/camera_exception.dart'; | |||||
| import 'types/features.dart'; | |||||
| /// UPC-E 1D format. | |||||
| upcE, | |||||
| /// UPC/EAN extension format. Not a stand-alone format. | |||||
| upcEanExtension | |||||
| } | |||||
| const _formatNames = <String, BarcodeFormat>{ | |||||
| '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, this.rawBytes); | |||||
| final String code; | |||||
| final BarcodeFormat format; | |||||
| /// Raw bytes are only supported by Android. | |||||
| final List<int> rawBytes; | |||||
| } | |||||
| typedef QRViewCreatedCallback = void Function(QRViewController); | |||||
| typedef PermissionSetCallback = void Function(QRViewController, bool); | |||||
| /// The [QRView] is the view where the camera | |||||
| /// and the barcode scanner gets displayed. | |||||
| class QRView extends StatefulWidget { | class QRView extends StatefulWidget { | ||||
| const QRView({ | const QRView({ | ||||
| @required Key key, | @required Key key, | ||||
| @required this.onQRViewCreated, | @required this.onQRViewCreated, | ||||
| this.overlay, | this.overlay, | ||||
| this.overlayMargin = EdgeInsets.zero, | this.overlayMargin = EdgeInsets.zero, | ||||
| this.cameraFacing = CameraFacing.back, | |||||
| this.onPermissionSet, | |||||
| this.formatsAllowed, | |||||
| }) : assert(key != null), | }) : assert(key != null), | ||||
| assert(onQRViewCreated != null), | assert(onQRViewCreated != null), | ||||
| super(key: key); | super(key: key); | ||||
| /// [onQRViewCreated] gets called when the view is created | |||||
| final QRViewCreatedCallback onQRViewCreated; | final QRViewCreatedCallback onQRViewCreated; | ||||
| /// Use [overlay] to provide an overlay for the view. | |||||
| /// This can be used to create a certain scan area. | |||||
| final ShapeBorder overlay; | final ShapeBorder overlay; | ||||
| /// Use [overlayMargin] to provide a margin to [overlay] | |||||
| final EdgeInsetsGeometry overlayMargin; | final EdgeInsetsGeometry overlayMargin; | ||||
| /// Set which camera to use on startup. | |||||
| /// | |||||
| /// [cameraFacing] can either be CameraFacing.front or CameraFacing.back. | |||||
| /// Defaults to CameraFacing.back | |||||
| final CameraFacing cameraFacing; | |||||
| /// Calls the provided [onPermissionSet] callback when the permission is set. | |||||
| final PermissionSetCallback onPermissionSet; | |||||
| /// Use [formatsAllowed] to specify which formats needs to be scanned. | |||||
| final List<BarcodeFormat> formatsAllowed; | |||||
| @override | @override | ||||
| State<StatefulWidget> createState() => _QRViewState(); | State<StatefulWidget> createState() => _QRViewState(); | ||||
| } | } | ||||
| class _QRViewState extends State<QRView> { | class _QRViewState extends State<QRView> { | ||||
| var _channel; | |||||
| @override | @override | ||||
| Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
| return NotificationListener( | |||||
| onNotification: onNotification, | |||||
| child: SizeChangedLayoutNotifier( | |||||
| child: (widget.overlay != null) | |||||
| ? _getPlatformQrViewWithOverlay() | |||||
| : _getPlatformQrView(), | |||||
| ), | |||||
| ); | |||||
| } | |||||
| bool onNotification(notification) { | |||||
| Future.microtask(() => { | |||||
| QRViewController.updateDimensions(widget.key, _channel, | |||||
| scanArea: widget.overlay != null | |||||
| ? (widget.overlay as QrScannerOverlayShape).cutOutSize | |||||
| : 0.0) | |||||
| }); | |||||
| return false; | |||||
| } | |||||
| Widget _getPlatformQrViewWithOverlay() { | |||||
| return Stack( | return Stack( | ||||
| children: [ | children: [ | ||||
| _getPlatformQrView(widget.key), | |||||
| if (widget.overlay != null) | |||||
| Container( | |||||
| padding: widget.overlayMargin, | |||||
| decoration: ShapeDecoration( | |||||
| shape: widget.overlay, | |||||
| ), | |||||
| ) | |||||
| else | |||||
| Container(), | |||||
| _getPlatformQrView(), | |||||
| Container( | |||||
| padding: widget.overlayMargin, | |||||
| decoration: ShapeDecoration( | |||||
| shape: widget.overlay, | |||||
| ), | |||||
| ) | |||||
| ], | ], | ||||
| ); | ); | ||||
| } | } | ||||
| Widget _getPlatformQrView(GlobalKey key) { | |||||
| Widget _getPlatformQrView() { | |||||
| Widget _platformQrView; | Widget _platformQrView; | ||||
| switch (defaultTargetPlatform) { | switch (defaultTargetPlatform) { | ||||
| case TargetPlatform.android: | case TargetPlatform.android: | ||||
| _platformQrView = AndroidView( | _platformQrView = AndroidView( | ||||
| viewType: 'net.touchcapture.qr.flutterqr/qrview', | viewType: 'net.touchcapture.qr.flutterqr/qrview', | ||||
| onPlatformViewCreated: _onPlatformViewCreated, | onPlatformViewCreated: _onPlatformViewCreated, | ||||
| creationParams: | |||||
| _QrCameraSettings(cameraFacing: widget.cameraFacing).toMap(), | |||||
| creationParamsCodec: StandardMessageCodec(), | |||||
| ); | ); | ||||
| break; | break; | ||||
| case TargetPlatform.iOS: | case TargetPlatform.iOS: | ||||
| @@ -142,8 +111,7 @@ class _QRViewState extends State<QRView> { | |||||
| viewType: 'net.touchcapture.qr.flutterqr/qrview', | viewType: 'net.touchcapture.qr.flutterqr/qrview', | ||||
| onPlatformViewCreated: _onPlatformViewCreated, | onPlatformViewCreated: _onPlatformViewCreated, | ||||
| creationParams: | creationParams: | ||||
| _CreationParams.fromWidget(MediaQuery.of(context).size.width, 400) | |||||
| .toMap(), | |||||
| _QrCameraSettings(cameraFacing: widget.cameraFacing).toMap(), | |||||
| creationParamsCodec: StandardMessageCodec(), | creationParamsCodec: StandardMessageCodec(), | ||||
| ); | ); | ||||
| break; | break; | ||||
| @@ -155,101 +123,178 @@ class _QRViewState extends State<QRView> { | |||||
| } | } | ||||
| void _onPlatformViewCreated(int id) { | void _onPlatformViewCreated(int id) { | ||||
| if (widget.onQRViewCreated == null) { | |||||
| return; | |||||
| } | |||||
| // We pass the cutout size so that the scanner respects the scan area. | // We pass the cutout size so that the scanner respects the scan area. | ||||
| var cutOutSize = 0.0; | var cutOutSize = 0.0; | ||||
| if (widget.overlay != null) { | if (widget.overlay != null) { | ||||
| cutOutSize = (widget.overlay as QrScannerOverlayShape).cutOutSize; | cutOutSize = (widget.overlay as QrScannerOverlayShape).cutOutSize; | ||||
| } | } | ||||
| widget.onQRViewCreated(QRViewController._(id, widget.key, cutOutSize)); | |||||
| } | |||||
| } | |||||
| _channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id'); | |||||
| class _CreationParams { | |||||
| _CreationParams({this.width, this.height}); | |||||
| // Start scan after creation of the view | |||||
| final controller = QRViewController._( | |||||
| _channel, widget.key, cutOutSize, widget.onPermissionSet) | |||||
| .._startScan(widget.key, cutOutSize, widget.formatsAllowed); | |||||
| static _CreationParams fromWidget(double width, double height) { | |||||
| return _CreationParams( | |||||
| width: width, | |||||
| height: height, | |||||
| ); | |||||
| // Initialize the controller for controlling the QRView | |||||
| if (widget.onQRViewCreated != null) { | |||||
| widget.onQRViewCreated(controller); | |||||
| } | |||||
| } | } | ||||
| } | |||||
| final double width; | |||||
| final double height; | |||||
| class _QrCameraSettings { | |||||
| _QrCameraSettings({ | |||||
| this.cameraFacing, | |||||
| }); | |||||
| final CameraFacing cameraFacing; | |||||
| Map<String, dynamic> toMap() { | Map<String, dynamic> toMap() { | ||||
| return <String, dynamic>{ | return <String, dynamic>{ | ||||
| 'width': width, | |||||
| 'height': height, | |||||
| 'cameraFacing': cameraFacing.index, | |||||
| }; | }; | ||||
| } | } | ||||
| } | } | ||||
| class QRViewController { | class QRViewController { | ||||
| QRViewController._(int id, GlobalKey qrKey, double scanArea) | |||||
| : _channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id') { | |||||
| updateDimensions(qrKey, scanArea: scanArea); | |||||
| _channel.setMethodCallHandler( | |||||
| (call) async { | |||||
| switch (call.method) { | |||||
| case scanMethodCall: | |||||
| if (call.arguments != null) { | |||||
| final args = call.arguments as Map; | |||||
| final code = args['code'] as String; | |||||
| final rawType = args['type'] as String; | |||||
| // Raw bytes are only supported by Android. | |||||
| final rawBytes = args['rawBytes'] as List<int>; | |||||
| final format = _formatNames[rawType]; | |||||
| if (format != null) { | |||||
| final barcode = Barcode(code, format, rawBytes); | |||||
| _scanUpdateController.sink.add(barcode); | |||||
| } else { | |||||
| throw Exception('Unexpected barcode type $rawType'); | |||||
| } | |||||
| QRViewController._(MethodChannel channel, GlobalKey qrKey, double scanArea, | |||||
| PermissionSetCallback onPermissionSet) | |||||
| : _channel = channel { | |||||
| _channel.setMethodCallHandler((call) async { | |||||
| switch (call.method) { | |||||
| case 'onRecognizeQR': | |||||
| if (call.arguments != null) { | |||||
| final args = call.arguments as Map; | |||||
| final code = args['code'] as String; | |||||
| final rawType = args['type'] as String; | |||||
| // Raw bytes are only supported by Android. | |||||
| final rawBytes = args['rawBytes'] as List<int>; | |||||
| final format = BarcodeTypesExtension.fromString(rawType); | |||||
| if (format != null) { | |||||
| final barcode = Barcode(code, format, rawBytes); | |||||
| _scanUpdateController.sink.add(barcode); | |||||
| } else { | |||||
| throw Exception('Unexpected barcode type $rawType'); | |||||
| } | } | ||||
| } | |||||
| }, | |||||
| ); | |||||
| } | |||||
| break; | |||||
| case 'onPermissionSet': | |||||
| await getSystemFeatures(); // if we have no permission all features will not be avaible | |||||
| if (call.arguments != null) { | |||||
| if (call.arguments as bool) { | |||||
| _hasPermissions = true; | |||||
| } else { | |||||
| _hasPermissions = false; | |||||
| } | |||||
| if (onPermissionSet != null) { | |||||
| onPermissionSet(this, call.arguments as bool); | |||||
| } | |||||
| } | |||||
| break; | |||||
| } | |||||
| }); | |||||
| } | } | ||||
| static const scanMethodCall = 'onRecognizeQR'; | |||||
| final MethodChannel _channel; | final MethodChannel _channel; | ||||
| final StreamController<Barcode> _scanUpdateController = | final StreamController<Barcode> _scanUpdateController = | ||||
| StreamController<Barcode>(); | StreamController<Barcode>(); | ||||
| Stream<Barcode> get scannedDataStream => _scanUpdateController.stream; | Stream<Barcode> get scannedDataStream => _scanUpdateController.stream; | ||||
| void flipCamera() { | |||||
| _channel.invokeMethod('flipCamera'); | |||||
| SystemFeatures _features; | |||||
| bool _hasPermissions; | |||||
| SystemFeatures get systemFeatures => _features; | |||||
| bool get hasPermissions => _hasPermissions; | |||||
| /// Starts the barcode scanner | |||||
| Future<void> _startScan(GlobalKey key, double cutOutSize, | |||||
| List<BarcodeFormat> barcodeFormats) async { | |||||
| // We need to update the dimension before the scan is started. | |||||
| QRViewController.updateDimensions(key, _channel, scanArea: cutOutSize); | |||||
| return _channel.invokeMethod( | |||||
| 'startScan', barcodeFormats?.map((e) => e.asInt())?.toList() ?? []); | |||||
| } | } | ||||
| void toggleFlash() { | |||||
| _channel.invokeMethod('toggleFlash'); | |||||
| /// Gets information about which camera is active. | |||||
| Future<CameraFacing> getCameraInfo() async { | |||||
| try { | |||||
| return CameraFacing | |||||
| .values[await _channel.invokeMethod('getCameraInfo') as int]; | |||||
| } on PlatformException catch (e) { | |||||
| throw CameraException(e.code, e.message); | |||||
| } | |||||
| } | |||||
| /// Flips the camera between available modes | |||||
| Future<CameraFacing> flipCamera() async { | |||||
| try { | |||||
| return CameraFacing | |||||
| .values[await _channel.invokeMethod('flipCamera') as int]; | |||||
| } on PlatformException catch (e) { | |||||
| throw CameraException(e.code, e.message); | |||||
| } | |||||
| } | } | ||||
| void pauseCamera() { | |||||
| _channel.invokeMethod('pauseCamera'); | |||||
| /// Get flashlight status | |||||
| Future<bool> getFlashStatus() async { | |||||
| try { | |||||
| return await _channel.invokeMethod('getFlashInfo'); | |||||
| } on PlatformException catch (e) { | |||||
| throw CameraException(e.code, e.message); | |||||
| } | |||||
| } | |||||
| /// Toggles the flashlight between available modes | |||||
| Future<void> toggleFlash() async { | |||||
| try { | |||||
| await _channel.invokeMethod('toggleFlash') as bool; | |||||
| } on PlatformException catch (e) { | |||||
| throw CameraException(e.code, e.message); | |||||
| } | |||||
| } | } | ||||
| void resumeCamera() { | |||||
| _channel.invokeMethod('resumeCamera'); | |||||
| /// Pauses barcode scanning | |||||
| Future<void> pauseCamera() async { | |||||
| try { | |||||
| await _channel.invokeMethod('pauseCamera'); | |||||
| } on PlatformException catch (e) { | |||||
| throw CameraException(e.code, e.message); | |||||
| } | |||||
| } | |||||
| /// Resumes barcode scanning | |||||
| Future<void> resumeCamera() async { | |||||
| try { | |||||
| await _channel.invokeMethod('resumeCamera'); | |||||
| } on PlatformException catch (e) { | |||||
| throw CameraException(e.code, e.message); | |||||
| } | |||||
| } | |||||
| /// Returns which features are available on device. | |||||
| Future<SystemFeatures> getSystemFeatures() async { | |||||
| try { | |||||
| var features = | |||||
| await _channel.invokeMapMethod<String, dynamic>('getSystemFeatures'); | |||||
| return SystemFeatures.fromJson(features); | |||||
| } on PlatformException catch (e) { | |||||
| throw CameraException(e.code, e.message); | |||||
| } | |||||
| } | } | ||||
| /// Disposes the barcode stream. | |||||
| void dispose() { | void dispose() { | ||||
| _scanUpdateController.close(); | _scanUpdateController.close(); | ||||
| } | } | ||||
| void updateDimensions(GlobalKey key, {double scanArea}) { | |||||
| /// Updates the view dimensions for iOS. | |||||
| static void updateDimensions(GlobalKey key, MethodChannel channel, | |||||
| {double scanArea}) { | |||||
| if (defaultTargetPlatform == TargetPlatform.iOS) { | if (defaultTargetPlatform == TargetPlatform.iOS) { | ||||
| final RenderBox renderBox = key.currentContext.findRenderObject(); | final RenderBox renderBox = key.currentContext.findRenderObject(); | ||||
| _channel.invokeMethod('setDimensions', { | |||||
| channel.invokeMethod('setDimensions', { | |||||
| 'width': renderBox.size.width, | 'width': renderBox.size.width, | ||||
| 'height': renderBox.size.height, | 'height': renderBox.size.height, | ||||
| 'scanArea': scanArea ?? 0 | 'scanArea': scanArea ?? 0 | ||||
| @@ -0,0 +1,16 @@ | |||||
| import 'barcode_format.dart'; | |||||
| /// The [Barcode] object holds information about the barcode or qr code. | |||||
| /// | |||||
| /// [code] is the content of the barcode. | |||||
| /// [format] displays which type the code is. | |||||
| /// Only for Android, [rawBytes] gives a list of bytes of the result. | |||||
| class Barcode { | |||||
| Barcode(this.code, this.format, this.rawBytes); | |||||
| final String code; | |||||
| final BarcodeFormat format; | |||||
| /// Raw bytes are only supported by Android. | |||||
| final List<int> rawBytes; | |||||
| } | |||||
| @@ -0,0 +1,174 @@ | |||||
| 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 | |||||
| } | |||||
| extension BarcodeTypesExtension on BarcodeFormat { | |||||
| int asInt() { | |||||
| return index; | |||||
| } | |||||
| static BarcodeFormat fromString(String format) { | |||||
| switch (format) { | |||||
| case 'AZTEC': | |||||
| return BarcodeFormat.aztec; | |||||
| break; | |||||
| case 'CODABAR': | |||||
| return BarcodeFormat.codabar; | |||||
| break; | |||||
| case 'CODE_39': | |||||
| return BarcodeFormat.code39; | |||||
| break; | |||||
| case 'CODE_93': | |||||
| return BarcodeFormat.code93; | |||||
| break; | |||||
| case 'CODE_128': | |||||
| return BarcodeFormat.code128; | |||||
| break; | |||||
| case 'DATA_MATRIX': | |||||
| return BarcodeFormat.dataMatrix; | |||||
| break; | |||||
| case 'EAN_8': | |||||
| return BarcodeFormat.ean8; | |||||
| break; | |||||
| case 'EAN_13': | |||||
| return BarcodeFormat.ean13; | |||||
| break; | |||||
| case 'ITF': | |||||
| return BarcodeFormat.itf; | |||||
| break; | |||||
| case 'MAXICODE': | |||||
| return BarcodeFormat.maxicode; | |||||
| break; | |||||
| case 'PDF_417': | |||||
| return BarcodeFormat.pdf417; | |||||
| break; | |||||
| case 'QR_CODE': | |||||
| return BarcodeFormat.qrcode; | |||||
| break; | |||||
| case 'RSS14': | |||||
| return BarcodeFormat.rss14; | |||||
| break; | |||||
| case 'RSS_EXPANDED': | |||||
| return BarcodeFormat.rssExpanded; | |||||
| break; | |||||
| case 'UPC_A': | |||||
| return BarcodeFormat.upcA; | |||||
| break; | |||||
| case 'UPC_E': | |||||
| return BarcodeFormat.upcE; | |||||
| break; | |||||
| case 'UPC_EAN_EXTENSION': | |||||
| return BarcodeFormat.upcEanExtension; | |||||
| break; | |||||
| default: | |||||
| return null; | |||||
| } | |||||
| } | |||||
| String get formatName { | |||||
| switch (this) { | |||||
| case BarcodeFormat.aztec: | |||||
| return 'AZTEC'; | |||||
| break; | |||||
| case BarcodeFormat.codabar: | |||||
| return 'CODABAR'; | |||||
| break; | |||||
| case BarcodeFormat.code39: | |||||
| return 'CODE_39'; | |||||
| break; | |||||
| case BarcodeFormat.code93: | |||||
| return 'CODE_93'; | |||||
| break; | |||||
| case BarcodeFormat.code128: | |||||
| return 'CODE_128'; | |||||
| break; | |||||
| case BarcodeFormat.dataMatrix: | |||||
| return 'DATA_MATRIX'; | |||||
| break; | |||||
| case BarcodeFormat.ean8: | |||||
| return 'EAN_8'; | |||||
| break; | |||||
| case BarcodeFormat.ean13: | |||||
| return 'EAN_13'; | |||||
| break; | |||||
| case BarcodeFormat.itf: | |||||
| return 'ITF'; | |||||
| break; | |||||
| case BarcodeFormat.maxicode: | |||||
| return 'MAXICODE'; | |||||
| break; | |||||
| case BarcodeFormat.pdf417: | |||||
| return 'PDF_417'; | |||||
| break; | |||||
| case BarcodeFormat.qrcode: | |||||
| return 'QR_CODE'; | |||||
| break; | |||||
| case BarcodeFormat.rss14: | |||||
| return 'RSS14'; | |||||
| break; | |||||
| case BarcodeFormat.rssExpanded: | |||||
| return 'RSS_EXPANDED'; | |||||
| break; | |||||
| case BarcodeFormat.upcA: | |||||
| return 'UPC_A'; | |||||
| break; | |||||
| case BarcodeFormat.upcE: | |||||
| return 'UPC_E'; | |||||
| break; | |||||
| case BarcodeFormat.upcEanExtension: | |||||
| return 'UPC_EAN_EXTENSION'; | |||||
| break; | |||||
| default: | |||||
| return 'NOT_VALID'; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,7 @@ | |||||
| enum CameraFacing { | |||||
| /// Shows back facing camera. | |||||
| back, | |||||
| /// Shows front facing camera. | |||||
| front | |||||
| } | |||||
| @@ -0,0 +1,14 @@ | |||||
| /// This is thrown when the plugin reports an error. | |||||
| class CameraException implements Exception { | |||||
| /// Creates a new camera exception with the given error code and description. | |||||
| CameraException(this.code, this.description); | |||||
| /// Error code. | |||||
| String code; | |||||
| /// Textual description of the error. | |||||
| String description; | |||||
| @override | |||||
| String toString() => 'CameraException($code, $description)'; | |||||
| } | |||||
| @@ -0,0 +1,13 @@ | |||||
| class SystemFeatures { | |||||
| SystemFeatures(this.hasFlash, this.hasBackCamera, this.hasFrontCamera); | |||||
| factory SystemFeatures.fromJson(Map<String, dynamic> features) => | |||||
| SystemFeatures( | |||||
| features['hasFlash'] ?? false, | |||||
| features['hasBackCamera'] ?? false, | |||||
| features['hasFrontCamera'] ?? false); | |||||
| final bool hasFlash; | |||||
| final bool hasFrontCamera; | |||||
| final bool hasBackCamera; | |||||
| } | |||||
| @@ -5,7 +5,7 @@ homepage: https://juliuscanute.com | |||||
| repository: https://github.com/juliuscanute/qr_code_scanner | repository: https://github.com/juliuscanute/qr_code_scanner | ||||
| environment: | environment: | ||||
| sdk: ">=2.3.0 <3.0.0" | |||||
| sdk: ">=2.6.0 <3.0.0" | |||||
| flutter: ^1.10.0 | flutter: ^1.10.0 | ||||
| dependencies: | dependencies: | ||||