| @@ -0,0 +1,24 @@ | |||
| --- | |||
| name: Bug report | |||
| about: Create a report to help us improve | |||
| title: "[BUG] " | |||
| labels: bug | |||
| assignees: '' | |||
| --- | |||
| **Describe the bug** | |||
| A clear and concise description of what the bug is. | |||
| **Flutter information** | |||
| Always provide the output of `flutter doctor -v` as it is needed in order to know on which Flutter versions the bug exists in. | |||
| **Device (please complete the following information):** | |||
| - Device: [e.g. iPhone6] | |||
| - OS: [e.g. iOS8.1] | |||
| **Screenshots** | |||
| If applicable, add screenshots to help explain your problem. | |||
| **Additional context** | |||
| Add any other context about the problem here. | |||
| @@ -0,0 +1,17 @@ | |||
| --- | |||
| name: Feature request | |||
| about: Suggest an idea for this project | |||
| title: "[FEATURE] " | |||
| labels: enhancement | |||
| assignees: '' | |||
| --- | |||
| **Is your feature request related to a problem? Please describe.** | |||
| A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] | |||
| **Describe the solution you'd like** | |||
| A clear and concise description of what you want to happen. | |||
| **Additional context** | |||
| Add any other context or screenshots about the feature request here. | |||
| @@ -1,3 +1,14 @@ | |||
| ## 0.1.0 | |||
| * Changed Android minSDKversion from 24 to 21 (#170) | |||
| * Fix preview size after iPad rotation (#125) | |||
| * Implemented Android Embedding V2 (#132) | |||
| * Added cutout bottom offset (#115) | |||
| * Fix Android ActivityLifecycleCallbacks (#166) | |||
| * Fix some other small bugs | |||
| ## 0.0.14 | |||
| * Fix disposing camera on iOS 14 (#113) | |||
| ## 0.0.13 | |||
| * Fix misalignment when QRView doesn't start from the top left (#45) | |||
| * Fix crash on iOS when scanning returns nil (#69, #72) | |||
| @@ -63,9 +63,20 @@ class _QRViewExampleState extends State<QRViewExample> { | |||
| children: <Widget>[ | |||
| Expanded( | |||
| flex: 5, | |||
| child: QRView( | |||
| key: qrKey, | |||
| onQRViewCreated: _onQRViewCreated, | |||
| // To ensure the Scanner view is properly sizes after rotation | |||
| // we need to listen for Flutter SizeChanged notification and update controller | |||
| child: NotificationListener<SizeChangedLayoutNotification>( | |||
| onNotification: (notification) { | |||
| Future.microtask(() => controller?.updateDimensions(qrKey)); | |||
| return false; | |||
| }, | |||
| child: SizeChangedLayoutNotifier( | |||
| key: const Key('qr-size-notifier'), | |||
| child: QRView( | |||
| key: qrKey, | |||
| onQRViewCreated: _onQRViewCreated, | |||
| ), | |||
| ), | |||
| ), | |||
| ), | |||
| Expanded( | |||
| @@ -120,16 +131,16 @@ controller.toggleFlash(); | |||
| ## Resume/Pause | |||
| Pause camera stream and scanner. | |||
| ```dart | |||
| controller.pause(); | |||
| controller.pauseCamera(); | |||
| ``` | |||
| Resume camera stream and scanner. | |||
| ```dart | |||
| controller.resume(); | |||
| controller.resumeCamera(); | |||
| ``` | |||
| # SDK | |||
| Requires at least SDK 24 (Android 7.0). | |||
| Requires at least SDK 21 (Android 5.0). | |||
| # TODOs | |||
| * iOS Native embedding is written to match what is supported in the framework as of the date of publication of this package. It needs to be improved as the framework support improves. | |||
| @@ -1,4 +1,5 @@ | |||
| include: package:pedantic/analysis_options.1.9.0.yaml | |||
| include: package:pedantic/analysis_options.yaml | |||
| linter: | |||
| rules: | |||
| - always_put_required_named_parameters_first | |||
| @@ -2,14 +2,14 @@ group 'net.touchcapture.qr.flutterqr' | |||
| version '1.0-SNAPSHOT' | |||
| buildscript { | |||
| ext.kotlin_version = '1.3.21' | |||
| ext.kotlin_version = '1.4.21' | |||
| repositories { | |||
| google() | |||
| jcenter() | |||
| } | |||
| dependencies { | |||
| classpath 'com.android.tools.build:gradle:3.3.2' | |||
| classpath 'com.android.tools.build:gradle:4.1.1' | |||
| classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | |||
| } | |||
| } | |||
| @@ -27,14 +27,15 @@ apply plugin: 'kotlin-android-extensions' | |||
| apply plugin: 'kotlin-kapt' | |||
| android { | |||
| compileSdkVersion 28 | |||
| compileSdkVersion 30 | |||
| sourceSets { | |||
| main.java.srcDirs += 'src/main/kotlin' | |||
| } | |||
| defaultConfig { | |||
| minSdkVersion 19 | |||
| // minSdkVersion is determined by Native View. | |||
| minSdkVersion 20 | |||
| testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | |||
| } | |||
| lintOptions { | |||
| @@ -48,6 +49,6 @@ android { | |||
| dependencies { | |||
| implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | |||
| implementation('com.journeyapps:zxing-android-embedded:4.1.0') { transitive = false } | |||
| implementation 'androidx.appcompat:appcompat:1.0.2' | |||
| implementation 'androidx.appcompat:appcompat:1.2.0' | |||
| implementation 'com.google.zxing:core:3.3.0' | |||
| } | |||
| @@ -1,25 +1,118 @@ | |||
| package net.touchcapture.qr.flutterqr | |||
| import android.Manifest | |||
| import android.content.pm.PackageManager | |||
| import android.os.Build | |||
| import androidx.annotation.NonNull | |||
| import io.flutter.embedding.engine.plugins.FlutterPlugin | |||
| import io.flutter.embedding.engine.plugins.activity.ActivityAware | |||
| import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding | |||
| import io.flutter.plugin.common.BinaryMessenger | |||
| import io.flutter.plugin.common.MethodCall | |||
| import io.flutter.plugin.common.MethodChannel.MethodCallHandler | |||
| import io.flutter.plugin.common.MethodChannel.Result | |||
| import io.flutter.plugin.common.PluginRegistry.Registrar | |||
| import io.flutter.plugin.common.PluginRegistry | |||
| import io.flutter.plugin.platform.PlatformViewRegistry | |||
| class FlutterQrPlugin : MethodCallHandler { | |||
| class FlutterQrPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { | |||
| var cameraPermissionContinuation: Runnable? = null | |||
| var requestingPermission = false | |||
| /** Plugin registration embedding v1 */ | |||
| companion object { | |||
| @JvmStatic | |||
| fun registerWith(registrar: Registrar) { | |||
| registrar | |||
| .platformViewRegistry() | |||
| .registerViewFactory( | |||
| "net.touchcapture.qr.flutterqr/qrview", QRViewFactory(registrar)) | |||
| fun registerWith(registrar: PluginRegistry.Registrar) { | |||
| FlutterQrPlugin().onAttachedToV1(registrar) | |||
| } | |||
| } | |||
| private fun onAttachedToV1(registrar: PluginRegistry.Registrar) { | |||
| Shared.activity = registrar.activity() | |||
| registrar.addRequestPermissionsResultListener(CameraRequestPermissionsListener()) | |||
| checkAndRequestPermission(null) | |||
| onAttachedToEngines(registrar.platformViewRegistry(), registrar.messenger()) | |||
| } | |||
| /** Plugin registration embedding v2 */ | |||
| override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { | |||
| onAttachedToEngines(flutterPluginBinding.platformViewRegistry, flutterPluginBinding.binaryMessenger) | |||
| } | |||
| override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { | |||
| } | |||
| /** Plugin start for both embedding v1 & v2 */ | |||
| private fun onAttachedToEngines(platformViewRegistry: PlatformViewRegistry, messenger: BinaryMessenger) { | |||
| platformViewRegistry | |||
| .registerViewFactory( | |||
| "net.touchcapture.qr.flutterqr/qrview", QRViewFactory(messenger)) | |||
| } | |||
| override fun onMethodCall(call: MethodCall, result: Result) { | |||
| when { | |||
| call.method == "getPlatformVersion" -> result.success("Android ${android.os.Build.VERSION.RELEASE}") | |||
| else -> result.notImplemented() | |||
| when (call.method) { | |||
| "checkAndRequestPermission" -> checkAndRequestPermission(result) | |||
| } | |||
| } | |||
| override fun onAttachedToActivity(activityPluginBinding: ActivityPluginBinding) { | |||
| Shared.activity = activityPluginBinding.activity | |||
| activityPluginBinding.addRequestPermissionsResultListener(CameraRequestPermissionsListener()) | |||
| checkAndRequestPermission(null) | |||
| } | |||
| override fun onDetachedFromActivityForConfigChanges() { | |||
| Shared.activity = null | |||
| } | |||
| override fun onReattachedToActivityForConfigChanges(activityPluginBinding: ActivityPluginBinding) { | |||
| Shared.activity = activityPluginBinding.activity | |||
| } | |||
| override fun onDetachedFromActivity() { | |||
| Shared.activity = null | |||
| } | |||
| private inner class CameraRequestPermissionsListener : PluginRegistry.RequestPermissionsResultListener { | |||
| override fun onRequestPermissionsResult(id: Int, permissions: Array<String>, grantResults: IntArray): Boolean { | |||
| if (id == QRView.CAMERA_REQUEST_ID && grantResults[0] == PackageManager.PERMISSION_GRANTED) { | |||
| cameraPermissionContinuation?.run() | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| } | |||
| private fun hasCameraPermission(): Boolean { | |||
| return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || | |||
| Shared.activity?.checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED | |||
| } | |||
| private fun checkAndRequestPermission(result: Result?) { | |||
| if (cameraPermissionContinuation != null) { | |||
| result?.error("cameraPermission", "Camera permission request ongoing", null) | |||
| } | |||
| cameraPermissionContinuation = Runnable { | |||
| cameraPermissionContinuation = null | |||
| if (!hasCameraPermission()) { | |||
| result?.error( | |||
| "cameraPermission", "MediaRecorderCamera permission not granted", null) | |||
| return@Runnable | |||
| } | |||
| } | |||
| requestingPermission = false | |||
| if (hasCameraPermission()) { | |||
| cameraPermissionContinuation?.run() | |||
| } else { | |||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | |||
| requestingPermission = true | |||
| Shared.activity?.requestPermissions( | |||
| arrayOf(Manifest.permission.CAMERA), | |||
| QRView.CAMERA_REQUEST_ID) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,12 +1,9 @@ | |||
| package net.touchcapture.qr.flutterqr | |||
| import android.Manifest | |||
| import android.app.Activity | |||
| import android.app.Application | |||
| import android.content.Context | |||
| import android.content.pm.PackageManager | |||
| import android.content.pm.PackageManager.PERMISSION_GRANTED | |||
| import android.os.Build | |||
| import android.os.Bundle | |||
| import android.view.View | |||
| import com.google.zxing.ResultPoint | |||
| @@ -14,56 +11,51 @@ import android.hardware.Camera.CameraInfo | |||
| import com.journeyapps.barcodescanner.BarcodeCallback | |||
| import com.journeyapps.barcodescanner.BarcodeResult | |||
| import com.journeyapps.barcodescanner.BarcodeView | |||
| import io.flutter.plugin.common.BinaryMessenger | |||
| import io.flutter.plugin.common.MethodCall | |||
| import io.flutter.plugin.common.MethodChannel | |||
| import io.flutter.plugin.common.PluginRegistry | |||
| import io.flutter.plugin.platform.PlatformView | |||
| class QRView(private val registrar: PluginRegistry.Registrar, id: Int) : | |||
| PlatformView,MethodChannel.MethodCallHandler { | |||
| class QRView(messenger: BinaryMessenger, id: Int, private val context: Context) : | |||
| PlatformView, MethodChannel.MethodCallHandler { | |||
| companion object { | |||
| const val CAMERA_REQUEST_ID = 513469796 | |||
| } | |||
| var barcodeView: BarcodeView? = null | |||
| private val activity = registrar.activity() | |||
| var cameraPermissionContinuation: Runnable? = null | |||
| var requestingPermission = false | |||
| private var isTorchOn: Boolean = false | |||
| val channel: MethodChannel | |||
| init { | |||
| registrar.addRequestPermissionsResultListener(CameraRequestPermissionsListener()) | |||
| channel = MethodChannel(registrar.messenger(), "net.touchcapture.qr.flutterqr/qrview_$id") | |||
| channel = MethodChannel(messenger, "net.touchcapture.qr.flutterqr/qrview_$id") | |||
| channel.setMethodCallHandler(this) | |||
| checkAndRequestPermission(null) | |||
| registrar.activity().application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks { | |||
| override fun onActivityPaused(p0: Activity?) { | |||
| if (p0 == registrar.activity()) { | |||
| Shared.activity?.application?.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks { | |||
| override fun onActivityPaused(p0: Activity) { | |||
| if (p0 == Shared.activity) { | |||
| barcodeView?.pause() | |||
| } | |||
| } | |||
| override fun onActivityResumed(p0: Activity?) { | |||
| if (p0 == registrar.activity()) { | |||
| override fun onActivityResumed(p0: Activity) { | |||
| if (p0 == Shared.activity) { | |||
| barcodeView?.resume() | |||
| } | |||
| } | |||
| override fun onActivityStarted(p0: Activity?) { | |||
| override fun onActivityStarted(p0: Activity) { | |||
| } | |||
| override fun onActivityDestroyed(p0: Activity?) { | |||
| override fun onActivityDestroyed(p0: Activity) { | |||
| } | |||
| override fun onActivitySaveInstanceState(p0: Activity?, p1: Bundle?) { | |||
| override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) { | |||
| } | |||
| override fun onActivityStopped(p0: Activity?) { | |||
| override fun onActivityStopped(p0: Activity) { | |||
| } | |||
| override fun onActivityCreated(p0: Activity?, p1: Bundle?) { | |||
| override fun onActivityCreated(p0: Activity, p1: Bundle?) { | |||
| } | |||
| }) | |||
| } | |||
| @@ -102,7 +94,7 @@ class QRView(private val registrar: PluginRegistry.Registrar, id: Int) : | |||
| } | |||
| private fun hasFlash(): Boolean { | |||
| return registrar.activeContext().packageManager | |||
| return context.packageManager | |||
| .hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH) | |||
| } | |||
| @@ -120,7 +112,7 @@ class QRView(private val registrar: PluginRegistry.Registrar, id: Int) : | |||
| } | |||
| private fun createBarCodeView(): BarcodeView? { | |||
| val barcode = BarcodeView(registrar.activity()) | |||
| val barcode = BarcodeView(Shared.activity) | |||
| barcode.decodeContinuous( | |||
| object : BarcodeCallback { | |||
| override fun barcodeResult(result: BarcodeResult) { | |||
| @@ -139,27 +131,8 @@ class QRView(private val registrar: PluginRegistry.Registrar, id: Int) : | |||
| barcodeView = null | |||
| } | |||
| private inner class CameraRequestPermissionsListener : PluginRegistry.RequestPermissionsResultListener { | |||
| override fun onRequestPermissionsResult(id: Int, permissions: Array<String>, grantResults: IntArray): Boolean { | |||
| if (id == CAMERA_REQUEST_ID && grantResults[0] == PERMISSION_GRANTED) { | |||
| cameraPermissionContinuation?.run() | |||
| return true | |||
| } | |||
| return false | |||
| } | |||
| } | |||
| private fun hasCameraPermission(): Boolean { | |||
| return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || | |||
| activity.checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED | |||
| } | |||
| override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { | |||
| when(call?.method){ | |||
| "checkAndRequestPermission" -> { | |||
| checkAndRequestPermission(result) | |||
| } | |||
| when(call.method){ | |||
| "flipCamera" -> { | |||
| flipCamera() | |||
| } | |||
| @@ -175,33 +148,6 @@ class QRView(private val registrar: PluginRegistry.Registrar, id: Int) : | |||
| } | |||
| } | |||
| private fun checkAndRequestPermission(result: MethodChannel.Result?) { | |||
| if (cameraPermissionContinuation != null) { | |||
| result?.error("cameraPermission", "Camera permission request ongoing", null); | |||
| } | |||
| cameraPermissionContinuation = Runnable { | |||
| cameraPermissionContinuation = null | |||
| if (!hasCameraPermission()) { | |||
| result?.error( | |||
| "cameraPermission", "MediaRecorderCamera permission not granted", null) | |||
| return@Runnable | |||
| } | |||
| } | |||
| requestingPermission = false | |||
| if (hasCameraPermission()) { | |||
| cameraPermissionContinuation?.run() | |||
| } else { | |||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | |||
| requestingPermission = true | |||
| registrar | |||
| .activity() | |||
| .requestPermissions( | |||
| arrayOf(Manifest.permission.CAMERA), | |||
| CAMERA_REQUEST_ID) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -1,17 +1,17 @@ | |||
| package net.touchcapture.qr.flutterqr | |||
| import android.content.Context | |||
| import io.flutter.plugin.common.PluginRegistry | |||
| import io.flutter.plugin.common.BinaryMessenger | |||
| import io.flutter.plugin.common.StandardMessageCodec | |||
| import io.flutter.plugin.platform.PlatformView | |||
| import io.flutter.plugin.platform.PlatformViewFactory | |||
| class QRViewFactory(private val registrar: PluginRegistry.Registrar) : | |||
| class QRViewFactory(private val messenger: BinaryMessenger) : | |||
| PlatformViewFactory(StandardMessageCodec.INSTANCE) { | |||
| override fun create(context: Context, id: Int, obj: Any?): PlatformView { | |||
| return QRView(registrar,id) | |||
| return QRView(messenger, id, context) | |||
| } | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| package net.touchcapture.qr.flutterqr | |||
| import android.app.Activity | |||
| import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding | |||
| object Shared { | |||
| var activity: Activity? = null | |||
| } | |||
| @@ -22,11 +22,15 @@ Demonstrates how to use the qr_code_scanner plugin. | |||
| ## Example: | |||
| ```dart | |||
| import 'package:flutter/material.dart'; | |||
| import 'package:flutter/services.dart'; | |||
| import 'package:qr_code_scanner/qr_code_scanner.dart'; | |||
| 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 { | |||
| const QRViewExample({ | |||
| Key key, | |||
| @@ -37,60 +41,154 @@ class QRViewExample extends StatefulWidget { | |||
| } | |||
| class _QRViewExampleState extends State<QRViewExample> { | |||
| final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); | |||
| var qrText = ""; | |||
| var qrText = ''; | |||
| var flashState = flashOn; | |||
| var cameraState = frontCamera; | |||
| QRViewController controller; | |||
| final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); | |||
| @override | |||
| Widget build(BuildContext context) { | |||
| return Scaffold( | |||
| body: Column( | |||
| children: <Widget>[ | |||
| Expanded(flex: 4, child: _buildQrView(context)), | |||
| Expanded( | |||
| child: QRView( | |||
| key: qrKey, | |||
| onQRViewCreated: _onQRViewCreated, | |||
| ), | |||
| flex: 4, | |||
| ), | |||
| Expanded( | |||
| child: Column(children: | |||
| <Widget>[ | |||
| Text("This is the result of scan: $qrText"), | |||
| RaisedButton( | |||
| onPressed: (){ | |||
| if(controller != null){ | |||
| controller.flipCamera(); | |||
| } | |||
| }, | |||
| child: Text( | |||
| 'Flip', | |||
| style: TextStyle(fontSize: 20) | |||
| flex: 1, | |||
| child: FittedBox( | |||
| fit: BoxFit.contain, | |||
| child: Column( | |||
| mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |||
| children: <Widget>[ | |||
| Text('This is the result of scan: $qrText'), | |||
| Row( | |||
| mainAxisAlignment: MainAxisAlignment.center, | |||
| crossAxisAlignment: CrossAxisAlignment.center, | |||
| children: <Widget>[ | |||
| Container( | |||
| margin: EdgeInsets.all(8), | |||
| 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)), | |||
| ), | |||
| ), | |||
| Container( | |||
| margin: EdgeInsets.all(8), | |||
| 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)), | |||
| ), | |||
| ) | |||
| ], | |||
| ), | |||
| ) | |||
| ], | |||
| Row( | |||
| mainAxisAlignment: MainAxisAlignment.center, | |||
| crossAxisAlignment: CrossAxisAlignment.center, | |||
| children: <Widget>[ | |||
| Container( | |||
| margin: EdgeInsets.all(8), | |||
| child: RaisedButton( | |||
| onPressed: () { | |||
| controller?.pauseCamera(); | |||
| }, | |||
| child: Text('pause', style: TextStyle(fontSize: 20)), | |||
| ), | |||
| ), | |||
| Container( | |||
| margin: EdgeInsets.all(8), | |||
| child: RaisedButton( | |||
| onPressed: () { | |||
| controller?.resumeCamera(); | |||
| }, | |||
| child: Text('resume', style: TextStyle(fontSize: 20)), | |||
| ), | |||
| ) | |||
| ], | |||
| ), | |||
| ], | |||
| ), | |||
| ), | |||
| flex: 1, | |||
| ) | |||
| ], | |||
| ), | |||
| ); | |||
| } | |||
| bool _isFlashOn(String current) { | |||
| return flashOn == current; | |||
| } | |||
| bool _isBackCamera(String current) { | |||
| return backCamera == current; | |||
| } | |||
| Widget _buildQrView(BuildContext context) { | |||
| // To ensure the Scanner view is properly sizes after rotation | |||
| // we need to listen for Flutter SizeChanged notification and update controller | |||
| return NotificationListener<SizeChangedLayoutNotification>( | |||
| onNotification: (notification) { | |||
| Future.microtask(() => controller?.updateDimensions(qrKey)); | |||
| 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: 300, | |||
| ), | |||
| ))); | |||
| } | |||
| void _onQRViewCreated(QRViewController controller) { | |||
| final channel = controller.channel; | |||
| controller.init(qrKey); | |||
| this.controller = controller; | |||
| channel.setMethodCallHandler((MethodCall call) async { | |||
| switch (call.method) { | |||
| case "onRecognizeQR": | |||
| dynamic arguments = call.arguments; | |||
| setState(() { | |||
| qrText = arguments.toString(); | |||
| }); | |||
| } | |||
| controller.scannedDataStream.listen((scanData) { | |||
| setState(() { | |||
| qrText = scanData; | |||
| }); | |||
| }); | |||
| } | |||
| @override | |||
| void dispose() { | |||
| controller.dispose(); | |||
| super.dispose(); | |||
| } | |||
| } | |||
| ``` | |||
| @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' | |||
| apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" | |||
| android { | |||
| compileSdkVersion 28 | |||
| compileSdkVersion 30 | |||
| sourceSets { | |||
| main.java.srcDirs += 'src/main/kotlin' | |||
| @@ -37,10 +37,10 @@ android { | |||
| } | |||
| defaultConfig { | |||
| // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). | |||
| applicationId "net.touchcapture.qr.flutterqrexample" | |||
| minSdkVersion 19 | |||
| targetSdkVersion 27 | |||
| // minSdkVersion is determined by Native View. | |||
| minSdkVersion 20 | |||
| targetSdkVersion 30 | |||
| versionCode flutterVersionCode.toInteger() | |||
| versionName flutterVersionName | |||
| testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | |||
| @@ -48,7 +48,6 @@ android { | |||
| buildTypes { | |||
| release { | |||
| // TODO: Add your own signing config for the release build. | |||
| // Signing with the debug keys for now, so `flutter run --release` works. | |||
| signingConfig signingConfigs.debug | |||
| } | |||
| @@ -61,7 +60,7 @@ flutter { | |||
| dependencies { | |||
| implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | |||
| testImplementation 'junit:junit:4.12' | |||
| testImplementation 'junit:junit:4.13.1' | |||
| androidTestImplementation 'com.android.support.test:runner:1.0.2' | |||
| androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' | |||
| } | |||
| @@ -13,7 +13,6 @@ | |||
| additional functionality it is fine to subclass or reimplement | |||
| FlutterApplication and put your custom class here. --> | |||
| <application | |||
| android:name="io.flutter.app.FlutterApplication" | |||
| android:label="flutter_qr_example" | |||
| android:icon="@mipmap/ic_launcher"> | |||
| <activity | |||
| @@ -23,13 +22,19 @@ | |||
| android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density" | |||
| android:hardwareAccelerated="true" | |||
| android:windowSoftInputMode="adjustResize"> | |||
| <!-- This keeps the window background of the activity showing | |||
| until Flutter renders its first frame. It can be removed if | |||
| there is no splash screen (such as the default splash screen | |||
| defined in @style/LaunchTheme). --> | |||
| <!-- Specify that the launch screen should continue being displayed --> | |||
| <!-- until Flutter renders its first frame. --> | |||
| <meta-data | |||
| android:name="io.flutter.app.android.SplashScreenUntilFirstFrame" | |||
| android:value="true" /> | |||
| android:name="io.flutter.embedding.android.SplashScreenDrawable" | |||
| android:resource="@drawable/launch_background" /> | |||
| <!-- Theme to apply as soon as Flutter begins rendering frames --> | |||
| <meta-data | |||
| android:name="io.flutter.embedding.android.NormalTheme" | |||
| android:resource="@style/NormalTheme" | |||
| /> | |||
| <meta-data | |||
| android:name="flutterEmbedding" | |||
| android:value="2" /> | |||
| <intent-filter> | |||
| <action android:name="android.intent.action.MAIN"/> | |||
| <category android:name="android.intent.category.LAUNCHER"/> | |||
| @@ -1,13 +1,6 @@ | |||
| package net.touchcapture.qr.flutterqrexample | |||
| import android.os.Bundle | |||
| import io.flutter.app.FlutterActivity | |||
| import io.flutter.plugins.GeneratedPluginRegistrant | |||
| import io.flutter.embedding.android.FlutterActivity | |||
| class MainActivity: FlutterActivity() { | |||
| override fun onCreate(savedInstanceState: Bundle?) { | |||
| super.onCreate(savedInstanceState) | |||
| GeneratedPluginRegistrant.registerWith(this) | |||
| } | |||
| } | |||
| @@ -1,8 +1,19 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <resources> | |||
| <!-- Theme applied to the Android Window while the process is starting --> | |||
| <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> | |||
| <!-- Show a splash screen on the activity. Automatically removed when | |||
| Flutter draws its first frame --> | |||
| <item name="android:windowBackground">@drawable/launch_background</item> | |||
| </style> | |||
| <!-- Theme applied to the Android Window as soon as the process has started. | |||
| This theme determines the color of the Android Window while your | |||
| Flutter UI initializes, as well as behind your Flutter UI while its | |||
| running. | |||
| This Theme is only used starting with V2 of Flutter's Android embedding. --> | |||
| <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> | |||
| <item name="android:windowBackground">@drawable/launch_background</item> | |||
| </style> | |||
| </resources> | |||
| @@ -1,12 +1,12 @@ | |||
| buildscript { | |||
| ext.kotlin_version = '1.3.50' | |||
| ext.kotlin_version = '1.4.21' | |||
| repositories { | |||
| google() | |||
| jcenter() | |||
| } | |||
| dependencies { | |||
| classpath 'com.android.tools.build:gradle:3.6.3' | |||
| classpath 'com.android.tools.build:gradle:4.1.1' | |||
| classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | |||
| } | |||
| } | |||
| @@ -1,3 +1,4 @@ | |||
| org.gradle.jvmargs=-Xmx1536M | |||
| android.useAndroidX=true | |||
| android.enableJetifier=true | |||
| android.enableR8=true | |||
| @@ -1,6 +1,6 @@ | |||
| #Thu May 14 12:19:05 CEST 2020 | |||
| #Thu Dec 10 21:49:57 CET 2020 | |||
| distributionBase=GRADLE_USER_HOME | |||
| distributionPath=wrapper/dists | |||
| zipStoreBase=GRADLE_USER_HOME | |||
| zipStorePath=wrapper/dists | |||
| distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip | |||
| distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip | |||
| @@ -4,69 +4,38 @@ | |||
| # CocoaPods analytics sends network stats synchronously affecting flutter build latency. | |||
| ENV['COCOAPODS_DISABLE_STATS'] = 'true' | |||
| project 'Runner', { | |||
| 'Debug' => :debug, | |||
| 'Profile' => :release, | |||
| 'Release' => :release, | |||
| } | |||
| def parse_KV_file(file, separator='=') | |||
| file_abs_path = File.expand_path(file) | |||
| if !File.exists? file_abs_path | |||
| return []; | |||
| def flutter_root | |||
| generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) | |||
| unless File.exist?(generated_xcode_build_settings_path) | |||
| raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" | |||
| end | |||
| File.foreach(generated_xcode_build_settings_path) do |line| | |||
| matches = line.match(/FLUTTER_ROOT\=(.*)/) | |||
| return matches[1].strip if matches | |||
| end | |||
| pods_ary = [] | |||
| skip_line_start_symbols = ["#", "/"] | |||
| File.foreach(file_abs_path) { |line| | |||
| next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } | |||
| plugin = line.split(pattern=separator) | |||
| if plugin.length == 2 | |||
| podname = plugin[0].strip() | |||
| path = plugin[1].strip() | |||
| podpath = File.expand_path("#{path}", file_abs_path) | |||
| pods_ary.push({:name => podname, :path => podpath}); | |||
| else | |||
| puts "Invalid plugin specification: #{line}" | |||
| end | |||
| } | |||
| return pods_ary | |||
| raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" | |||
| end | |||
| target 'Runner' do | |||
| use_frameworks! | |||
| require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) | |||
| # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock | |||
| # referring to absolute paths on developers' machines. | |||
| system('rm -rf .symlinks') | |||
| system('mkdir -p .symlinks/plugins') | |||
| flutter_ios_podfile_setup | |||
| # Flutter Pods | |||
| generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') | |||
| if generated_xcode_build_settings.empty? | |||
| puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." | |||
| end | |||
| generated_xcode_build_settings.map { |p| | |||
| if p[:name] == 'FLUTTER_FRAMEWORK_DIR' | |||
| symlink = File.join('.symlinks', 'flutter') | |||
| File.symlink(File.dirname(p[:path]), symlink) | |||
| pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) | |||
| end | |||
| } | |||
| target 'Runner' do | |||
| use_frameworks! | |||
| use_modular_headers! | |||
| # Plugin Pods | |||
| plugin_pods = parse_KV_file('../.flutter-plugins') | |||
| plugin_pods.map { |p| | |||
| symlink = File.join('.symlinks', 'plugins', p[:name]) | |||
| File.symlink(p[:path], symlink) | |||
| pod p[:name], :path => File.join(symlink, 'ios') | |||
| } | |||
| flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) | |||
| end | |||
| post_install do |installer| | |||
| installer.pods_project.targets.each do |target| | |||
| target.build_configurations.each do |config| | |||
| config.build_settings['ENABLE_BITCODE'] = 'NO' | |||
| end | |||
| flutter_additional_ios_build_settings(target) | |||
| end | |||
| end | |||
| @@ -9,12 +9,8 @@ | |||
| /* Begin PBXBuildFile section */ | |||
| 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; | |||
| 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; | |||
| 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; | |||
| 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; | |||
| 72CA4F8FC83C02C1B0C5292A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D115C5DD3945F9758AB33CE2 /* Pods_Runner.framework */; }; | |||
| 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; | |||
| 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; | |||
| 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; | |||
| 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; | |||
| 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; | |||
| 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; | |||
| @@ -27,8 +23,6 @@ | |||
| dstPath = ""; | |||
| dstSubfolderSpec = 10; | |||
| files = ( | |||
| 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, | |||
| 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, | |||
| ); | |||
| name = "Embed Frameworks"; | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| @@ -39,18 +33,19 @@ | |||
| 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>"; }; | |||
| 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; | |||
| 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; }; | |||
| 4465DBB6FCA236431DE34346 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/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>"; }; | |||
| 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | |||
| 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; | |||
| 87B204D37F3832F9BE4979B0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Pods/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>"; }; | |||
| 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; | |||
| 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; }; | |||
| 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; 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>"; }; | |||
| 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | |||
| 9994328F3BE8CB44F875A29E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | |||
| D115C5DD3945F9758AB33CE2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| /* End PBXFileReference section */ | |||
| @@ -59,8 +54,6 @@ | |||
| isa = PBXFrameworksBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, | |||
| 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, | |||
| 72CA4F8FC83C02C1B0C5292A /* Pods_Runner.framework in Frameworks */, | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| @@ -71,6 +64,9 @@ | |||
| 0972BCF1757D16A75C3EC240 /* Pods */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 4465DBB6FCA236431DE34346 /* Pods-Runner.debug.xcconfig */, | |||
| 9994328F3BE8CB44F875A29E /* Pods-Runner.release.xcconfig */, | |||
| 87B204D37F3832F9BE4979B0 /* Pods-Runner.profile.xcconfig */, | |||
| ); | |||
| name = Pods; | |||
| sourceTree = "<group>"; | |||
| @@ -78,9 +74,7 @@ | |||
| 9740EEB11CF90186004384FC /* Flutter */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 3B80C3931E831B6300D905FE /* App.framework */, | |||
| 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, | |||
| 9740EEBA1CF902C7004384FC /* Flutter.framework */, | |||
| 9740EEB21CF90195004384FC /* Debug.xcconfig */, | |||
| 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, | |||
| 9740EEB31CF90195004384FC /* Generated.xcconfig */, | |||
| @@ -174,6 +168,7 @@ | |||
| TargetAttributes = { | |||
| 97C146ED1CF9000F007C117D = { | |||
| CreatedOnToolsVersion = 7.3.1; | |||
| DevelopmentTeam = 5LSCTACHT8; | |||
| LastSwiftMigration = 1020; | |||
| ProvisioningStyle = Automatic; | |||
| }; | |||
| @@ -224,7 +219,7 @@ | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| shellPath = /bin/sh; | |||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; | |||
| shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; | |||
| }; | |||
| 3C208BB7148E708611B40C3B /* [CP] Check Pods Manifest.lock */ = { | |||
| isa = PBXShellScriptBuildPhase; | |||
| @@ -253,17 +248,13 @@ | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| ); | |||
| inputFileListPaths = ( | |||
| ); | |||
| inputPaths = ( | |||
| "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", | |||
| "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", | |||
| "${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"; | |||
| outputFileListPaths = ( | |||
| ); | |||
| outputPaths = ( | |||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", | |||
| "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", | |||
| @@ -271,7 +262,7 @@ | |||
| ); | |||
| runOnlyForDeploymentPostprocessing = 0; | |||
| shellPath = /bin/sh; | |||
| shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | |||
| shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | |||
| showEnvVarsInLog = 0; | |||
| }; | |||
| 9740EEB61CF901F6004384FC /* Run Script */ = { | |||
| @@ -324,7 +315,6 @@ | |||
| /* Begin XCBuildConfiguration section */ | |||
| 249021D3217E4FDB00AE95B9 /* Profile */ = { | |||
| isa = XCBuildConfiguration; | |||
| baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; | |||
| buildSettings = { | |||
| ALWAYS_SEARCH_USER_PATHS = NO; | |||
| CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; | |||
| @@ -382,7 +372,7 @@ | |||
| CODE_SIGN_IDENTITY = "iPhone Developer"; | |||
| CODE_SIGN_STYLE = Automatic; | |||
| CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | |||
| DEVELOPMENT_TEAM = ""; | |||
| DEVELOPMENT_TEAM = 5LSCTACHT8; | |||
| ENABLE_BITCODE = NO; | |||
| FRAMEWORK_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| @@ -394,7 +384,7 @@ | |||
| "$(inherited)", | |||
| "$(PROJECT_DIR)/Flutter", | |||
| ); | |||
| PRODUCT_BUNDLE_IDENTIFIER = net.touch.capture; | |||
| PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.capture; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| PROVISIONING_PROFILE_SPECIFIER = ""; | |||
| SWIFT_VERSION = 4.0; | |||
| @@ -404,7 +394,6 @@ | |||
| }; | |||
| 97C147031CF9000F007C117D /* Debug */ = { | |||
| isa = XCBuildConfiguration; | |||
| baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; | |||
| buildSettings = { | |||
| ALWAYS_SEARCH_USER_PATHS = NO; | |||
| CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; | |||
| @@ -462,7 +451,6 @@ | |||
| }; | |||
| 97C147041CF9000F007C117D /* Release */ = { | |||
| isa = XCBuildConfiguration; | |||
| baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; | |||
| buildSettings = { | |||
| ALWAYS_SEARCH_USER_PATHS = NO; | |||
| CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; | |||
| @@ -522,7 +510,7 @@ | |||
| CODE_SIGN_IDENTITY = "iPhone Developer"; | |||
| CODE_SIGN_STYLE = Automatic; | |||
| CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | |||
| DEVELOPMENT_TEAM = ""; | |||
| DEVELOPMENT_TEAM = 5LSCTACHT8; | |||
| ENABLE_BITCODE = NO; | |||
| FRAMEWORK_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| @@ -534,7 +522,7 @@ | |||
| "$(inherited)", | |||
| "$(PROJECT_DIR)/Flutter", | |||
| ); | |||
| PRODUCT_BUNDLE_IDENTIFIER = net.touch.capture; | |||
| PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.capture; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| PROVISIONING_PROFILE_SPECIFIER = ""; | |||
| SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | |||
| @@ -553,7 +541,7 @@ | |||
| CODE_SIGN_IDENTITY = "iPhone Developer"; | |||
| CODE_SIGN_STYLE = Automatic; | |||
| CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | |||
| DEVELOPMENT_TEAM = ""; | |||
| DEVELOPMENT_TEAM = 5LSCTACHT8; | |||
| ENABLE_BITCODE = NO; | |||
| FRAMEWORK_SEARCH_PATHS = ( | |||
| "$(inherited)", | |||
| @@ -565,7 +553,7 @@ | |||
| "$(inherited)", | |||
| "$(PROJECT_DIR)/Flutter", | |||
| ); | |||
| PRODUCT_BUNDLE_IDENTIFIER = net.touch.capture; | |||
| PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.capture; | |||
| PRODUCT_NAME = "$(TARGET_NAME)"; | |||
| PROVISIONING_PROFILE_SPECIFIER = ""; | |||
| SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | |||
| @@ -1,8 +0,0 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
| <plist version="1.0"> | |||
| <dict> | |||
| <key>BuildSystemType</key> | |||
| <string>Original</string> | |||
| </dict> | |||
| </plist> | |||
| @@ -37,20 +37,7 @@ class _QRViewExampleState extends State<QRViewExample> { | |||
| return Scaffold( | |||
| body: Column( | |||
| children: <Widget>[ | |||
| Expanded( | |||
| flex: 4, | |||
| child: QRView( | |||
| key: qrKey, | |||
| onQRViewCreated: _onQRViewCreated, | |||
| overlay: QrScannerOverlayShape( | |||
| borderColor: Colors.red, | |||
| borderRadius: 10, | |||
| borderLength: 30, | |||
| borderWidth: 10, | |||
| cutOutSize: 300, | |||
| ), | |||
| ), | |||
| ), | |||
| Expanded(flex: 4, child: _buildQrView(context)), | |||
| Expanded( | |||
| flex: 1, | |||
| child: FittedBox( | |||
| @@ -148,6 +135,29 @@ class _QRViewExampleState extends State<QRViewExample> { | |||
| return backCamera == current; | |||
| } | |||
| Widget _buildQrView(BuildContext context) { | |||
| // To ensure the Scanner view is properly sizes after rotation | |||
| // we need to listen for Flutter SizeChanged notification and update controller | |||
| return NotificationListener<SizeChangedLayoutNotification>( | |||
| onNotification: (notification) { | |||
| Future.microtask(() => controller?.updateDimensions(qrKey)); | |||
| 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: 300, | |||
| ), | |||
| ))); | |||
| } | |||
| void _onQRViewCreated(QRViewController controller) { | |||
| this.controller = controller; | |||
| controller.scannedDataStream.listen((scanData) { | |||
| @@ -3,7 +3,7 @@ description: Demonstrates how to use the flutter_qr plugin. | |||
| publish_to: 'none' | |||
| environment: | |||
| sdk: ">=2.0.0-dev.68.0 <3.0.0" | |||
| sdk: ">=2.3.0 <3.0.0" | |||
| dependencies: | |||
| flutter: | |||
| @@ -23,7 +23,7 @@ public class QRView:NSObject,FlutterPlatformView { | |||
| func isCameraAvailable(success: Bool) -> Void { | |||
| if success { | |||
| do { | |||
| try scanner?.startScanning(resultBlock: { codes in | |||
| try scanner?.startScanning(resultBlock: { [weak self] codes in | |||
| if let codes = codes { | |||
| for code in codes { | |||
| var typeString: String; | |||
| @@ -56,7 +56,7 @@ public class QRView:NSObject,FlutterPlatformView { | |||
| guard let stringValue = code.stringValue else { continue } | |||
| let result = ["code": stringValue, "type": typeString] | |||
| self.channel.invokeMethod("onRecognizeQR", arguments: result) | |||
| self?.channel.invokeMethod("onRecognizeQR", arguments: result) | |||
| } | |||
| } | |||
| }) | |||
| @@ -73,7 +73,7 @@ public class QRView:NSObject,FlutterPlatformView { | |||
| [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in | |||
| switch(call.method){ | |||
| case "setDimensions": | |||
| var 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) | |||
| case "flipCamera": | |||
| self?.flipCamera() | |||
| @@ -92,9 +92,16 @@ public class QRView:NSObject,FlutterPlatformView { | |||
| } | |||
| func setDimensions(width: Double, height: Double) -> Void { | |||
| previewView.frame = CGRect(x: 0, y: 0, width: width, height: height) | |||
| scanner = MTBBarcodeScanner(previewView: previewView) | |||
| MTBBarcodeScanner.requestCameraPermission(success: isCameraAvailable) | |||
| previewView.frame = CGRect(x: 0, y: 0, width: width, height: height) | |||
| if let sc: MTBBarcodeScanner = scanner { | |||
| if let previewLayer = sc.previewLayer { | |||
| previewLayer.frame = previewView.bounds; | |||
| } | |||
| } else { | |||
| scanner = MTBBarcodeScanner(previewView: previewView) | |||
| MTBBarcodeScanner.requestCameraPermission(success: isCameraAvailable) | |||
| } | |||
| } | |||
| func flipCamera(){ | |||
| @@ -17,7 +17,7 @@ public class QRViewFactory: NSObject, FlutterPlatformViewFactory { | |||
| } | |||
| public func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { | |||
| var dictionary = args as! Dictionary<String, Double> | |||
| 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) | |||
| } | |||
| @@ -91,6 +91,7 @@ class QRView extends StatefulWidget { | |||
| @required Key key, | |||
| @required this.onQRViewCreated, | |||
| this.overlay, | |||
| this.overlayMargin = EdgeInsets.zero, | |||
| }) : assert(key != null), | |||
| assert(onQRViewCreated != null), | |||
| super(key: key); | |||
| @@ -98,6 +99,7 @@ class QRView extends StatefulWidget { | |||
| final QRViewCreatedCallback onQRViewCreated; | |||
| final ShapeBorder overlay; | |||
| final EdgeInsetsGeometry overlayMargin; | |||
| @override | |||
| State<StatefulWidget> createState() => _QRViewState(); | |||
| @@ -111,6 +113,7 @@ class _QRViewState extends State<QRView> { | |||
| _getPlatformQrView(), | |||
| if (widget.overlay != null) | |||
| Container( | |||
| padding: widget.overlayMargin, | |||
| decoration: ShapeDecoration( | |||
| shape: widget.overlay, | |||
| ), | |||
| @@ -177,11 +180,7 @@ class _CreationParams { | |||
| class QRViewController { | |||
| QRViewController._(int id, GlobalKey qrKey) | |||
| : _channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id') { | |||
| if (defaultTargetPlatform == TargetPlatform.iOS) { | |||
| final RenderBox renderBox = qrKey.currentContext.findRenderObject(); | |||
| _channel.invokeMethod('setDimensions', | |||
| {'width': renderBox.size.width, 'height': renderBox.size.height}); | |||
| } | |||
| updateDimensions(qrKey); | |||
| _channel.setMethodCallHandler( | |||
| (call) async { | |||
| switch (call.method) { | |||
| @@ -231,4 +230,12 @@ class QRViewController { | |||
| void dispose() { | |||
| _scanUpdateController.close(); | |||
| } | |||
| void updateDimensions(GlobalKey key) { | |||
| if (defaultTargetPlatform == TargetPlatform.iOS) { | |||
| final RenderBox renderBox = key.currentContext.findRenderObject(); | |||
| _channel.invokeMethod('setDimensions', | |||
| {'width': renderBox.size.width, 'height': renderBox.size.height}); | |||
| } | |||
| } | |||
| } | |||
| @@ -10,6 +10,7 @@ class QrScannerOverlayShape extends ShapeBorder { | |||
| this.borderRadius = 0, | |||
| this.borderLength = 40, | |||
| this.cutOutSize = 250, | |||
| this.cutOutBottomOffset = 0, | |||
| }) : assert( | |||
| cutOutSize != null ?? | |||
| cutOutSize != null ?? | |||
| @@ -22,6 +23,7 @@ class QrScannerOverlayShape extends ShapeBorder { | |||
| final double borderRadius; | |||
| final double borderLength; | |||
| final double cutOutSize; | |||
| final double cutOutBottomOffset; | |||
| @override | |||
| EdgeInsetsGeometry get dimensions => const EdgeInsets.all(10); | |||
| @@ -86,7 +88,11 @@ class QrScannerOverlayShape extends ShapeBorder { | |||
| final cutOutRect = Rect.fromLTWH( | |||
| rect.left + width / 2 - _cutOutSize / 2 + borderOffset, | |||
| rect.top + height / 2 - _cutOutSize / 2 + borderOffset, | |||
| -cutOutBottomOffset + | |||
| rect.top + | |||
| height / 2 - | |||
| _cutOutSize / 2 + | |||
| borderOffset, | |||
| _cutOutSize - borderOffset * 2, | |||
| _cutOutSize - borderOffset * 2, | |||
| ); | |||
| @@ -1,6 +1,6 @@ | |||
| name: qr_code_scanner | |||
| description: QR code scanner that can be embedded inside flutter. It uses zxing in Android and MTBBarcode scanner in iOS. | |||
| version: 0.0.13 | |||
| version: 0.1.0 | |||
| author: Julius Canute <juliuscanute at touchcapture.net> | |||
| homepage: https://juliuscanute.com | |||
| repository: https://github.com/juliuscanute/qr_code_scanner | |||
| @@ -14,7 +14,7 @@ dependencies: | |||
| sdk: flutter | |||
| dev_dependencies: | |||
| pedantic: ^1.9.0 | |||
| pedantic: ^1.9.2 | |||
| flutter: | |||
| plugin: | |||