diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..2393c04 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -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. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..864a505 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -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. diff --git a/.resources/android_back_camera_flash.gif b/.resources/android_back_camera_flash.gif deleted file mode 100644 index d20a3b9..0000000 Binary files a/.resources/android_back_camera_flash.gif and /dev/null differ diff --git a/.resources/android_front_camera_scan.gif b/.resources/android_front_camera_scan.gif deleted file mode 100644 index f6d150a..0000000 Binary files a/.resources/android_front_camera_scan.gif and /dev/null differ diff --git a/.resources/ios_back_camera_flash.gif b/.resources/ios_back_camera_flash.gif deleted file mode 100644 index d6c9ec1..0000000 Binary files a/.resources/ios_back_camera_flash.gif and /dev/null differ diff --git a/.resources/ios_front_camera_scan.gif b/.resources/ios_front_camera_scan.gif deleted file mode 100644 index 721842a..0000000 Binary files a/.resources/ios_front_camera_scan.gif and /dev/null differ diff --git a/CHANGELOG.md b/CHANGELOG.md index b3d9a14..1585540 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/README.md b/README.md index cc2991e..6050aed 100644 --- a/README.md +++ b/README.md @@ -63,9 +63,20 @@ class _QRViewExampleState extends State { children: [ 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( + 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. diff --git a/analysis_options.yaml b/analysis_options.yaml index e2dba57..180afc8 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -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 diff --git a/android/build.gradle b/android/build.gradle index a5cb848..437c6b4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -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' } diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt index 9336335..ca59f08 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/FlutterQrPlugin.kt @@ -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, 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) + } } } } diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt index d3b9145..e91743d 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRView.kt @@ -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, 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) - } - } - } } diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt index f9be661..a797084 100644 --- a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/QRViewFactory.kt @@ -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) } } \ No newline at end of file diff --git a/android/src/main/kotlin/net/touchcapture/qr/flutterqr/Shared.kt b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/Shared.kt new file mode 100644 index 0000000..7823e01 --- /dev/null +++ b/android/src/main/kotlin/net/touchcapture/qr/flutterqr/Shared.kt @@ -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 +} \ No newline at end of file diff --git a/example/README.md b/example/README.md index 2ec5a26..4fc9d62 100644 --- a/example/README.md +++ b/example/README.md @@ -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 { - 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: [ + Expanded(flex: 4, child: _buildQrView(context)), Expanded( - child: QRView( - key: qrKey, - onQRViewCreated: _onQRViewCreated, - ), - flex: 4, - ), - Expanded( - child: Column(children: - [ - 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: [ + Text('This is the result of scan: $qrText'), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + 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: [ + 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( + 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(); + } } + ``` diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index f738ff8..a2c7d06 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -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' } diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 8a40bfb..3407805 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -13,7 +13,6 @@ additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> - + + + android:name="io.flutter.embedding.android.SplashScreenDrawable" + android:resource="@drawable/launch_background" /> + + + diff --git a/example/android/app/src/main/kotlin/net/touchcapture/qr/flutterqrexample/MainActivity.kt b/example/android/app/src/main/kotlin/net/touchcapture/qr/flutterqrexample/MainActivity.kt index fcdee63..3a276af 100644 --- a/example/android/app/src/main/kotlin/net/touchcapture/qr/flutterqrexample/MainActivity.kt +++ b/example/android/app/src/main/kotlin/net/touchcapture/qr/flutterqrexample/MainActivity.kt @@ -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) - } } diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 00fa441..3257023 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,8 +1,19 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle index e7e0b02..b198b37 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -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" } } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 94adc3a..a673820 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.enableR8=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index aee9768..8d5215f 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/example/ios/Podfile b/example/ios/Podfile index 799e2b4..1e8c3c9 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -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 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index e84dd13..1bb97b9 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -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 = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 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 = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 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 = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 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 = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 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 = ""; }; 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 = ""; @@ -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"; diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index 949b678..0000000 --- a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - BuildSystemType - Original - - diff --git a/example/lib/main.dart b/example/lib/main.dart index 052d32e..653fbc0 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -37,20 +37,7 @@ class _QRViewExampleState extends State { return Scaffold( body: Column( children: [ - 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 { 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( + 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) { diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 02dae87..6df5ea4 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -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: diff --git a/ios/Classes/QRView.swift b/ios/Classes/QRView.swift index 45fb3d1..eeb3bfd 100644 --- a/ios/Classes/QRView.swift +++ b/ios/Classes/QRView.swift @@ -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 + let arguments = call.arguments as! Dictionary 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(){ diff --git a/ios/Classes/QRViewFactory.swift b/ios/Classes/QRViewFactory.swift index 35442de..fff745b 100644 --- a/ios/Classes/QRViewFactory.swift +++ b/ios/Classes/QRViewFactory.swift @@ -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 + let dictionary = args as! Dictionary return QRView(withFrame: CGRect(x: 0, y: 0, width: dictionary["width"] ?? 0, height: dictionary["height"] ?? 0), withRegistrar: registrar!,withId: viewId) } diff --git a/lib/src/qr_code_scanner.dart b/lib/src/qr_code_scanner.dart index 852d7f6..451f240 100644 --- a/lib/src/qr_code_scanner.dart +++ b/lib/src/qr_code_scanner.dart @@ -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 createState() => _QRViewState(); @@ -111,6 +113,7 @@ class _QRViewState extends State { _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}); + } + } } diff --git a/lib/src/qr_scanner_overlay_shape.dart b/lib/src/qr_scanner_overlay_shape.dart index 886c533..77c1a14 100644 --- a/lib/src/qr_scanner_overlay_shape.dart +++ b/lib/src/qr_scanner_overlay_shape.dart @@ -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, ); diff --git a/pubspec.yaml b/pubspec.yaml index bf98b2b..0cbfd2c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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 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: