Sander Roest
Committed by GitHub

Merge branch 'juliansteenbakker:master' into master

1 -## NEXT 1 +## 6.0.2
  2 +
  3 +Bugs fixed:
  4 +* Fixed a bug that prevented `analyzeImage` from actually accepting the configured formats.
  5 +
  6 +Improvements:
  7 +* [iOS] Excluded the `arm64` architecture for Simulators, which is unsupported by MLKit 7.0.0.
  8 +
  9 +## 6.0.1
  10 +
  11 +Bugs fixed:
  12 +* Fixed a bug that would cause onDetect to not handle errors.
  13 +
  14 +Improvements:
  15 +* [iOS] Excluded the `armv7` architecture, which is unsupported by MLKit 7.0.0.
  16 +* Added a new `onDetectError` error handler to the `MobileScanner` widget, for use with `onDetect`.
  17 +
  18 +## 6.0.0
  19 +
  20 +**BREAKING CHANGES:**
  21 +
  22 +* [iOS] iOS 15.5.0 is now the minimum supported iOS version.
  23 +* [iOS] Updates MLKit to version 7.0.0.
  24 +* [iOS] Updates the minimum supported XCode version to 15.3.0.
2 25
3 Improvements: 26 Improvements:
4 * [MacOS] Added the corners and size information to barcode results. 27 * [MacOS] Added the corners and size information to barcode results.
@@ -60,6 +60,10 @@ dev.steenbakker.mobile_scanner.useUnbundled=true @@ -60,6 +60,10 @@ dev.steenbakker.mobile_scanner.useUnbundled=true
60 ``` 60 ```
61 61
62 ### iOS 62 ### iOS
  63 +
  64 +_iOS arm64 Simulators are currently not yet supported, until the migration to the Vision API is complete._
  65 +_See_ https://github.com/juliansteenbakker/mobile_scanner/issues/1225
  66 +
63 **Add the following keys to your Info.plist file, located in <project root>/ios/Runner/Info.plist:** 67 **Add the following keys to your Info.plist file, located in <project root>/ios/Runner/Info.plist:**
64 NSCameraUsageDescription - describe why your app needs access to the camera. This is called Privacy - Camera Usage Description in the visual editor. 68 NSCameraUsageDescription - describe why your app needs access to the camera. This is called Privacy - Camera Usage Description in the visual editor.
65 69
@@ -27,12 +27,12 @@ android { @@ -27,12 +27,12 @@ android {
27 compileSdk 34 27 compileSdk 34
28 28
29 compileOptions { 29 compileOptions {
30 - sourceCompatibility JavaVersion.VERSION_1_8  
31 - targetCompatibility JavaVersion.VERSION_1_8 30 + sourceCompatibility JavaVersion.VERSION_17
  31 + targetCompatibility JavaVersion.VERSION_17
32 } 32 }
33 33
34 kotlinOptions { 34 kotlinOptions {
35 - jvmTarget = '1.8' 35 + jvmTarget = '17'
36 } 36 }
37 37
38 sourceSets { 38 sourceSets {
@@ -42,7 +42,7 @@ android { @@ -42,7 +42,7 @@ android {
42 defaultConfig { 42 defaultConfig {
43 // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 43 // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
44 applicationId "dev.steenbakker.mobile_scanner_example" 44 applicationId "dev.steenbakker.mobile_scanner_example"
45 - minSdkVersion 21 45 + minSdkVersion 24
46 targetSdkVersion 34 46 targetSdkVersion 34
47 versionCode flutterVersionCode.toInteger() 47 versionCode flutterVersionCode.toInteger()
48 versionName flutterVersionName 48 versionName flutterVersionName
1 #Thu May 02 10:24:49 CEST 2024 1 #Thu May 02 10:24:49 CEST 2024
2 distributionBase=GRADLE_USER_HOME 2 distributionBase=GRADLE_USER_HOME
3 distributionPath=wrapper/dists 3 distributionPath=wrapper/dists
4 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip 4 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
5 zipStoreBase=GRADLE_USER_HOME 5 zipStoreBase=GRADLE_USER_HOME
6 zipStorePath=wrapper/dists 6 zipStorePath=wrapper/dists
@@ -18,8 +18,8 @@ pluginManagement { @@ -18,8 +18,8 @@ pluginManagement {
18 18
19 plugins { 19 plugins {
20 id "dev.flutter.flutter-plugin-loader" version "1.0.0" 20 id "dev.flutter.flutter-plugin-loader" version "1.0.0"
21 - id "com.android.application" version "7.3.0" apply false  
22 - id "org.jetbrains.kotlin.android" version "1.7.22" apply false 21 + id "com.android.application" version "8.3.2" apply false
  22 + id "org.jetbrains.kotlin.android" version "2.0.20" apply false
23 } 23 }
24 24
25 include ":app" 25 include ":app"
@@ -21,6 +21,6 @@ @@ -21,6 +21,6 @@
21 <key>CFBundleVersion</key> 21 <key>CFBundleVersion</key>
22 <string>1.0</string> 22 <string>1.0</string>
23 <key>MinimumOSVersion</key> 23 <key>MinimumOSVersion</key>
24 - <string>12.0</string> 24 + <string>15.5.0</string>
25 </dict> 25 </dict>
26 </plist> 26 </plist>
1 # Uncomment this line to define a global platform for your project 1 # Uncomment this line to define a global platform for your project
2 -# platform :ios, '12.0' 2 +# platform :ios, '15.5.0'
3 3
4 # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -40,8 +40,8 @@ end @@ -40,8 +40,8 @@ end
40 post_install do |installer| 40 post_install do |installer|
41 installer.pods_project.targets.each do |target| 41 installer.pods_project.targets.each do |target|
42 flutter_additional_ios_build_settings(target) 42 flutter_additional_ios_build_settings(target)
43 - target.build_configurations.each do |config|  
44 - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'  
45 - end 43 + target.build_configurations.each do |config|
  44 + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.5.0'
  45 + end
46 end 46 end
47 end 47 end
@@ -470,7 +470,7 @@ @@ -470,7 +470,7 @@
470 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 470 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
471 GCC_WARN_UNUSED_FUNCTION = YES; 471 GCC_WARN_UNUSED_FUNCTION = YES;
472 GCC_WARN_UNUSED_VARIABLE = YES; 472 GCC_WARN_UNUSED_VARIABLE = YES;
473 - IPHONEOS_DEPLOYMENT_TARGET = 12.0; 473 + IPHONEOS_DEPLOYMENT_TARGET = 15.5.0;
474 MTL_ENABLE_DEBUG_INFO = NO; 474 MTL_ENABLE_DEBUG_INFO = NO;
475 SDKROOT = iphoneos; 475 SDKROOT = iphoneos;
476 SUPPORTED_PLATFORMS = iphoneos; 476 SUPPORTED_PLATFORMS = iphoneos;
@@ -601,7 +601,7 @@ @@ -601,7 +601,7 @@
601 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 601 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
602 GCC_WARN_UNUSED_FUNCTION = YES; 602 GCC_WARN_UNUSED_FUNCTION = YES;
603 GCC_WARN_UNUSED_VARIABLE = YES; 603 GCC_WARN_UNUSED_VARIABLE = YES;
604 - IPHONEOS_DEPLOYMENT_TARGET = 12.0; 604 + IPHONEOS_DEPLOYMENT_TARGET = 15.5.0;
605 MTL_ENABLE_DEBUG_INFO = YES; 605 MTL_ENABLE_DEBUG_INFO = YES;
606 ONLY_ACTIVE_ARCH = YES; 606 ONLY_ACTIVE_ARCH = YES;
607 SDKROOT = iphoneos; 607 SDKROOT = iphoneos;
@@ -650,7 +650,7 @@ @@ -650,7 +650,7 @@
650 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 650 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
651 GCC_WARN_UNUSED_FUNCTION = YES; 651 GCC_WARN_UNUSED_FUNCTION = YES;
652 GCC_WARN_UNUSED_VARIABLE = YES; 652 GCC_WARN_UNUSED_VARIABLE = YES;
653 - IPHONEOS_DEPLOYMENT_TARGET = 12.0; 653 + IPHONEOS_DEPLOYMENT_TARGET = 15.5.0;
654 MTL_ENABLE_DEBUG_INFO = NO; 654 MTL_ENABLE_DEBUG_INFO = NO;
655 SDKROOT = iphoneos; 655 SDKROOT = iphoneos;
656 SUPPORTED_PLATFORMS = iphoneos; 656 SUPPORTED_PLATFORMS = iphoneos;
@@ -131,15 +131,15 @@ class ScannerOverlay extends CustomPainter { @@ -131,15 +131,15 @@ class ScannerOverlay extends CustomPainter {
131 131
132 @override 132 @override
133 void paint(Canvas canvas, Size size) { 133 void paint(Canvas canvas, Size size) {
134 - // TODO: use `Offset.zero & size` instead of Rect.largest  
135 // we need to pass the size to the custom paint widget 134 // we need to pass the size to the custom paint widget
136 - final backgroundPath = Path()..addRect(Rect.largest); 135 + final backgroundPath = Path()
  136 + ..addRect(Rect.fromLTWH(0, 0, size.width, size.height));
137 final cutoutPath = Path()..addRect(scanWindow); 137 final cutoutPath = Path()..addRect(scanWindow);
138 138
139 final backgroundPaint = Paint() 139 final backgroundPaint = Paint()
140 ..color = Colors.black.withOpacity(0.5) 140 ..color = Colors.black.withOpacity(0.5)
141 ..style = PaintingStyle.fill 141 ..style = PaintingStyle.fill
142 - ..blendMode = BlendMode.dstOut; 142 + ..blendMode = BlendMode.dstOver;
143 143
144 final backgroundWithCutout = Path.combine( 144 final backgroundWithCutout = Path.combine(
145 PathOperation.difference, 145 PathOperation.difference,
@@ -102,9 +102,9 @@ class ScannerOverlay extends CustomPainter { @@ -102,9 +102,9 @@ class ScannerOverlay extends CustomPainter {
102 102
103 @override 103 @override
104 void paint(Canvas canvas, Size size) { 104 void paint(Canvas canvas, Size size) {
105 - // TODO: use `Offset.zero & size` instead of Rect.largest  
106 // we need to pass the size to the custom paint widget 105 // we need to pass the size to the custom paint widget
107 - final backgroundPath = Path()..addRect(Rect.largest); 106 + final backgroundPath = Path()
  107 + ..addRect(Rect.fromLTWH(0, 0, size.width, size.height));
108 108
109 final cutoutPath = Path() 109 final cutoutPath = Path()
110 ..addRRect( 110 ..addRRect(
@@ -120,7 +120,7 @@ class ScannerOverlay extends CustomPainter { @@ -120,7 +120,7 @@ class ScannerOverlay extends CustomPainter {
120 final backgroundPaint = Paint() 120 final backgroundPaint = Paint()
121 ..color = Colors.black.withOpacity(0.5) 121 ..color = Colors.black.withOpacity(0.5)
122 ..style = PaintingStyle.fill 122 ..style = PaintingStyle.fill
123 - ..blendMode = BlendMode.dstOut; 123 + ..blendMode = BlendMode.dstOver;
124 124
125 final backgroundWithCutout = Path.combine( 125 final backgroundWithCutout = Path.combine(
126 PathOperation.difference, 126 PathOperation.difference,
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 # 4 #
5 Pod::Spec.new do |s| 5 Pod::Spec.new do |s|
6 s.name = 'mobile_scanner' 6 s.name = 'mobile_scanner'
7 - s.version = '5.2.3' 7 + s.version = '6.0.2'
8 s.summary = 'An universal scanner for Flutter based on MLKit.' 8 s.summary = 'An universal scanner for Flutter based on MLKit.'
9 s.description = <<-DESC 9 s.description = <<-DESC
10 An universal scanner for Flutter based on MLKit. 10 An universal scanner for Flutter based on MLKit.
@@ -15,11 +15,16 @@ An universal scanner for Flutter based on MLKit. @@ -15,11 +15,16 @@ An universal scanner for Flutter based on MLKit.
15 s.source = { :path => '.' } 15 s.source = { :path => '.' }
16 s.source_files = 'Classes/**/*' 16 s.source_files = 'Classes/**/*'
17 s.dependency 'Flutter' 17 s.dependency 'Flutter'
18 - s.dependency 'GoogleMLKit/BarcodeScanning', '~> 6.0.0'  
19 - s.platform = :ios, '12.0' 18 + s.dependency 'GoogleMLKit/BarcodeScanning', '~> 7.0.0'
  19 + s.platform = :ios, '15.5.0'
20 s.static_framework = true 20 s.static_framework = true
21 - # Flutter.framework does not contain a i386 slice.  
22 - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 21 + # Flutter.framework does not contain a i386 slice, and MLKit does not support armv7.
  22 + s.pod_target_xcconfig = {
  23 + 'DEFINES_MODULE' => 'YES',
  24 + # TODO: add back arm64 (and armv7?) when switching to the Vision API.
  25 + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386 armv7 arm64',
  26 + 'EXCLUDED_ARCHS[sdk=iphoneos*]' => 'armv7',
  27 + }
23 s.swift_version = '5.0' 28 s.swift_version = '5.0'
24 s.resource_bundles = { 'mobile_scanner_privacy' => ['Resources/PrivacyInfo.xcprivacy'] } 29 s.resource_bundles = { 'mobile_scanner_privacy' => ['Resources/PrivacyInfo.xcprivacy'] }
25 end 30 end
@@ -21,6 +21,7 @@ class MobileScanner extends StatefulWidget { @@ -21,6 +21,7 @@ class MobileScanner extends StatefulWidget {
21 const MobileScanner({ 21 const MobileScanner({
22 this.controller, 22 this.controller,
23 this.onDetect, 23 this.onDetect,
  24 + this.onDetectError = _onDetectErrorHandler,
24 this.fit = BoxFit.cover, 25 this.fit = BoxFit.cover,
25 this.errorBuilder, 26 this.errorBuilder,
26 this.overlayBuilder, 27 this.overlayBuilder,
@@ -34,15 +35,17 @@ class MobileScanner extends StatefulWidget { @@ -34,15 +35,17 @@ class MobileScanner extends StatefulWidget {
34 final MobileScannerController? controller; 35 final MobileScannerController? controller;
35 36
36 /// The function that signals when new codes were detected by the [controller]. 37 /// The function that signals when new codes were detected by the [controller].
37 - /// If null, use the controller.barcodes stream directly to capture barcodes.  
38 - ///  
39 - /// This method does not receive any [MobileScannerBarcodeException]s  
40 - /// that are emitted by the scanner.  
41 /// 38 ///
42 /// To handle both [BarcodeCapture]s and [MobileScannerBarcodeException]s, 39 /// To handle both [BarcodeCapture]s and [MobileScannerBarcodeException]s,
43 - /// use the [MobileScannerController.barcodes] stream directly. 40 + /// use the [MobileScannerController.barcodes] stream directly (recommended),
  41 + /// or provide a function to [onDetectError].
44 final void Function(BarcodeCapture barcodes)? onDetect; 42 final void Function(BarcodeCapture barcodes)? onDetect;
45 43
  44 + /// The error handler equivalent for the [onDetect] function.
  45 + ///
  46 + /// If [onDetect] is not null, and this is null, errors are silently ignored.
  47 + final void Function(Object error, StackTrace stackTrace) onDetectError;
  48 +
46 /// The error builder for the camera preview. 49 /// The error builder for the camera preview.
47 /// 50 ///
48 /// If this is null, a black [ColoredBox], 51 /// If this is null, a black [ColoredBox],
@@ -122,6 +125,11 @@ class MobileScanner extends StatefulWidget { @@ -122,6 +125,11 @@ class MobileScanner extends StatefulWidget {
122 125
123 @override 126 @override
124 State<MobileScanner> createState() => _MobileScannerState(); 127 State<MobileScanner> createState() => _MobileScannerState();
  128 +
  129 + /// This empty function is used as the default error handler for [onDetect].
  130 + static void _onDetectErrorHandler(Object error, StackTrace stackTrace) {
  131 + // Do nothing.
  132 + }
125 } 133 }
126 134
127 class _MobileScannerState extends State<MobileScanner> 135 class _MobileScannerState extends State<MobileScanner>
@@ -255,7 +263,11 @@ class _MobileScannerState extends State<MobileScanner> @@ -255,7 +263,11 @@ class _MobileScannerState extends State<MobileScanner>
255 void initState() { 263 void initState() {
256 if (widget.onDetect != null) { 264 if (widget.onDetect != null) {
257 WidgetsBinding.instance.addObserver(this); 265 WidgetsBinding.instance.addObserver(this);
258 - _subscription = controller.barcodes.listen(widget.onDetect); 266 + _subscription = controller.barcodes.listen(
  267 + widget.onDetect,
  268 + onError: widget.onDetectError,
  269 + cancelOnError: false,
  270 + );
259 } 271 }
260 if (controller.autoStart) { 272 if (controller.autoStart) {
261 controller.start(); 273 controller.start();
@@ -297,7 +309,11 @@ class _MobileScannerState extends State<MobileScanner> @@ -297,7 +309,11 @@ class _MobileScannerState extends State<MobileScanner>
297 case AppLifecycleState.paused: 309 case AppLifecycleState.paused:
298 return; 310 return;
299 case AppLifecycleState.resumed: 311 case AppLifecycleState.resumed:
300 - _subscription = controller.barcodes.listen(widget.onDetect); 312 + _subscription = controller.barcodes.listen(
  313 + widget.onDetect,
  314 + onError: widget.onDetectError,
  315 + cancelOnError: false,
  316 + );
301 317
302 unawaited(controller.start()); 318 unawaited(controller.start());
303 case AppLifecycleState.inactive: 319 case AppLifecycleState.inactive:
@@ -186,6 +186,8 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { @@ -186,6 +186,8 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
186 /// Analyze an image file. 186 /// Analyze an image file.
187 /// 187 ///
188 /// The [path] points to a file on the device. 188 /// The [path] points to a file on the device.
  189 + /// The [formats] specify the barcode formats that should be detected in the image.
  190 + /// If the [formats] are omitted or empty, all formats are detected.
189 /// 191 ///
190 /// This is only supported on Android, iOS and MacOS. 192 /// This is only supported on Android, iOS and MacOS.
191 /// 193 ///
@@ -193,8 +195,11 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> { @@ -193,8 +195,11 @@ class MobileScannerController extends ValueNotifier<MobileScannerState> {
193 /// 195 ///
194 /// If an error occurred during the analysis of the image, 196 /// If an error occurred during the analysis of the image,
195 /// a [MobileScannerBarcodeException] error is thrown. 197 /// a [MobileScannerBarcodeException] error is thrown.
196 - Future<BarcodeCapture?> analyzeImage(String path) {  
197 - return MobileScannerPlatform.instance.analyzeImage(path); 198 + Future<BarcodeCapture?> analyzeImage(
  199 + String path, {
  200 + List<BarcodeFormat> formats = const <BarcodeFormat>[],
  201 + }) {
  202 + return MobileScannerPlatform.instance.analyzeImage(path, formats: formats);
198 } 203 }
199 204
200 /// Build a camera preview widget. 205 /// Build a camera preview widget.
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 # 4 #
5 Pod::Spec.new do |s| 5 Pod::Spec.new do |s|
6 s.name = 'mobile_scanner' 6 s.name = 'mobile_scanner'
7 - s.version = '5.2.3' 7 + s.version = '6.0.2'
8 s.summary = 'An universal scanner for Flutter based on MLKit.' 8 s.summary = 'An universal scanner for Flutter based on MLKit.'
9 s.description = <<-DESC 9 s.description = <<-DESC
10 An universal scanner for Flutter based on MLKit. 10 An universal scanner for Flutter based on MLKit.
1 name: mobile_scanner 1 name: mobile_scanner
2 description: A universal barcode and QR code scanner for Flutter based on MLKit. Uses CameraX on Android, AVFoundation on iOS and Apple Vision & AVFoundation on macOS. 2 description: A universal barcode and QR code scanner for Flutter based on MLKit. Uses CameraX on Android, AVFoundation on iOS and Apple Vision & AVFoundation on macOS.
3 -version: 5.2.3 3 +version: 6.0.2
4 repository: https://github.com/juliansteenbakker/mobile_scanner 4 repository: https://github.com/juliansteenbakker/mobile_scanner
5 5
6 screenshots: 6 screenshots: