diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6e73bd477..2622377a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,18 @@
-## 5.0.0-beta.2
+## 5.0.0-beta.3
**BREAKING CHANGES:**
-* Flutter 3.16.0 is now required.
+* Flutter 3.19.0 is now required.
+* [iOS] iOS 12.0 is now the minimum supported iOS version.
+* [iOS] Adds a Privacy Manifest.
+
+Bugs fixed:
+* Fixed an issue where the camera preview and barcode scanner did not work the second time on web.
+
+Improvements:
+* [web] Migrates to extension types. (thanks @koji-1009 !)
+
+## 5.0.0-beta.2
Bugs fixed:
* Fixed an issue where the scan window was not updated when its size was changed. (thanks @navaronbracke !)
diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle
index 2ff64859c..85e5e3b25 100644
--- a/example/android/app/build.gradle
+++ b/example/android/app/build.gradle
@@ -62,7 +62,3 @@ android {
flutter {
source '../..'
}
-
-dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-}
diff --git a/example/android/build.gradle b/example/android/build.gradle
index 6554ae975..bc157bd1a 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -1,16 +1,3 @@
-buildscript {
- ext.kotlin_version = '1.9.22'
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:8.2.2'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
-}
-
allprojects {
repositories {
google()
diff --git a/example/android/settings.gradle b/example/android/settings.gradle
index 05dfe8308..26f682fa1 100644
--- a/example/android/settings.gradle
+++ b/example/android/settings.gradle
@@ -5,12 +5,21 @@ pluginManagement {
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
- }
- settings.ext.flutterSdkPath = flutterSdkPath()
+ }()
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
- includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
}
-include ":app"
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "8.2.2" apply false
+ id "org.jetbrains.kotlin.android" version "1.9.22" apply false
+}
-apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+include ":app"
\ No newline at end of file
diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist
index 9625e105d..7c5696400 100644
--- a/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/example/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 11.0
+ 12.0
diff --git a/example/ios/Podfile b/example/ios/Podfile
index 1dfb86970..5905f045b 100644
--- a/example/ios/Podfile
+++ b/example/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
-# platform :ios, '11.0'
+# platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -41,7 +41,7 @@ post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
- config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
+ config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
end
end
end
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index da346373e..f3ebff98f 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -452,7 +452,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -583,7 +583,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -632,7 +632,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index f2824b01c..cffa3350c 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -6,8 +6,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.0.1
environment:
- sdk: ">=3.2.0 <4.0.0"
- flutter: ">=3.16.0"
+ sdk: ">=3.3.0 <4.0.0"
+ flutter: ">=3.19.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
diff --git a/ios/Resources/PrivacyInfo.xcprivacy b/ios/Resources/PrivacyInfo.xcprivacy
new file mode 100644
index 000000000..a1f9119d1
--- /dev/null
+++ b/ios/Resources/PrivacyInfo.xcprivacy
@@ -0,0 +1,14 @@
+
+
+
+
+ NSPrivacyAccessedAPITypes
+
+ NSPrivacyCollectedDataTypes
+
+ NSPrivacyTrackingDomains
+
+ NSPrivacyTracking
+
+
+
diff --git a/ios/mobile_scanner.podspec b/ios/mobile_scanner.podspec
index 12785ac52..c0522bec2 100644
--- a/ios/mobile_scanner.podspec
+++ b/ios/mobile_scanner.podspec
@@ -4,21 +4,22 @@
#
Pod::Spec.new do |s|
s.name = 'mobile_scanner'
- s.version = '3.5.6'
+ s.version = '5.0.0'
s.summary = 'An universal scanner for Flutter based on MLKit.'
s.description = <<-DESC
An universal scanner for Flutter based on MLKit.
DESC
- s.homepage = 'http://example.com'
+ s.homepage = 'https://github.com/juliansteenbakker/mobile_scanner'
s.license = { :file => '../LICENSE' }
- s.author = { 'Your Company' => 'email@example.com' }
+ s.author = { 'Julian Steenbakker' => 'juliansteenbakker@outlook.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.dependency 'Flutter'
- s.dependency 'GoogleMLKit/BarcodeScanning', '~> 4.0.0'
- s.platform = :ios, '11.0'
+ s.dependency 'GoogleMLKit/BarcodeScanning', '~> 5.0.0'
+ s.platform = :ios, '12.0'
s.static_framework = true
# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
s.swift_version = '5.0'
+ s.resource_bundles = { 'mobile_scanner_privacy' => ['Resources/PrivacyInfo.xcprivacy'] }
end
diff --git a/lib/src/method_channel/mobile_scanner_method_channel.dart b/lib/src/method_channel/mobile_scanner_method_channel.dart
index cd0174024..a9d49336c 100644
--- a/lib/src/method_channel/mobile_scanner_method_channel.dart
+++ b/lib/src/method_channel/mobile_scanner_method_channel.dart
@@ -94,12 +94,29 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
///
/// Throws a [MobileScannerException] if the permission is not granted.
Future _requestCameraPermission() async {
- final MobileScannerAuthorizationState authorizationState;
-
try {
- authorizationState = MobileScannerAuthorizationState.fromRawValue(
+ final MobileScannerAuthorizationState authorizationState =
+ MobileScannerAuthorizationState.fromRawValue(
await methodChannel.invokeMethod('state') ?? 0,
);
+
+ switch (authorizationState) {
+ // Authorization was already granted, no need to request it again.
+ case MobileScannerAuthorizationState.authorized:
+ return;
+ // Android does not have an undetermined authorization state.
+ // So if the permission was denied, request it again.
+ case MobileScannerAuthorizationState.denied:
+ case MobileScannerAuthorizationState.undetermined:
+ final bool permissionGranted =
+ await methodChannel.invokeMethod('request') ?? false;
+
+ if (!permissionGranted) {
+ throw const MobileScannerException(
+ errorCode: MobileScannerErrorCode.permissionDenied,
+ );
+ }
+ }
} on PlatformException catch (error) {
// If the permission state is invalid, that is an error.
throw MobileScannerException(
@@ -111,37 +128,6 @@ class MethodChannelMobileScanner extends MobileScannerPlatform {
),
);
}
-
- switch (authorizationState) {
- case MobileScannerAuthorizationState.authorized:
- return; // Already authorized.
- // Android does not have an undetermined authorization state.
- // So if the permission was denied, request it again.
- case MobileScannerAuthorizationState.denied:
- case MobileScannerAuthorizationState.undetermined:
- try {
- final bool granted =
- await methodChannel.invokeMethod('request') ?? false;
-
- if (granted) {
- return; // Authorization was granted.
- }
-
- throw const MobileScannerException(
- errorCode: MobileScannerErrorCode.permissionDenied,
- );
- } on PlatformException catch (error) {
- // If the permission state is invalid, that is an error.
- throw MobileScannerException(
- errorCode: MobileScannerErrorCode.genericError,
- errorDetails: MobileScannerErrorDetails(
- code: error.code,
- details: error.details as Object?,
- message: error.message,
- ),
- );
- }
- }
}
@override
diff --git a/lib/src/mobile_scanner_controller.dart b/lib/src/mobile_scanner_controller.dart
index af7297ca8..bfa221d3f 100644
--- a/lib/src/mobile_scanner_controller.dart
+++ b/lib/src/mobile_scanner_controller.dart
@@ -223,12 +223,14 @@ class MobileScannerController extends ValueNotifier {
}
/// Start scanning for barcodes.
- /// Upon calling this method, the necessary camera permission will be requested.
///
/// The [cameraDirection] can be used to specify the camera direction.
/// If this is null, this defaults to the [facing] value.
///
/// Does nothing if the camera is already running.
+ /// Upon calling this method, the necessary camera permission will be requested.
+ ///
+ /// If the permission is denied on iOS, MacOS or Web, there is no way to request it again.
Future start({CameraFacing? cameraDirection}) async {
if (_isDisposed) {
throw const MobileScannerException(
@@ -240,6 +242,13 @@ class MobileScannerController extends ValueNotifier {
);
}
+ // Permission was denied, do nothing.
+ // When the controller is stopped,
+ // the error is reset so the permission can be requested again if possible.
+ if (value.error?.errorCode == MobileScannerErrorCode.permissionDenied) {
+ return;
+ }
+
// Do nothing if the camera is already running.
if (value.isRunning) {
return;
diff --git a/lib/src/web/barcode_reader.dart b/lib/src/web/barcode_reader.dart
index f301af426..42fc894ba 100644
--- a/lib/src/web/barcode_reader.dart
+++ b/lib/src/web/barcode_reader.dart
@@ -62,20 +62,19 @@ abstract class BarcodeReader {
final Completer completer = Completer();
- final HTMLScriptElement script =
- (document.createElement('script') as HTMLScriptElement)
- ..id = scriptId
- ..async = true
- ..defer = false
- ..type = 'application/javascript'
- ..lang = 'javascript'
- ..crossOrigin = 'anonymous'
- ..src = alternateScriptUrl ?? scriptUrl
- ..onload = (JSAny _) {
- if (!completer.isCompleted) {
- completer.complete();
- }
- }.toJS;
+ final HTMLScriptElement script = HTMLScriptElement()
+ ..id = scriptId
+ ..async = true
+ ..defer = false
+ ..type = 'application/javascript'
+ ..lang = 'javascript'
+ ..crossOrigin = 'anonymous'
+ ..src = alternateScriptUrl ?? scriptUrl
+ ..onload = (JSAny _) {
+ if (!completer.isCompleted) {
+ completer.complete();
+ }
+ }.toJS;
script.onerror = (JSAny _) {
if (!completer.isCompleted) {
diff --git a/lib/src/web/javascript_map.dart b/lib/src/web/javascript_map.dart
index e096ada89..5b4457641 100644
--- a/lib/src/web/javascript_map.dart
+++ b/lib/src/web/javascript_map.dart
@@ -9,12 +9,10 @@ import 'dart:js_interop';
///
/// Object literals can be made using [jsify].
@JS('Map')
-@staticInterop
-class JSMap {
+extension type JSMap._(JSObject _)
+ implements JSObject {
external factory JSMap();
-}
-extension JSMapExtension on JSMap {
external V? get(K key);
external JSVoid set(K key, V? value);
}
diff --git a/lib/src/web/media_track_constraints_delegate.dart b/lib/src/web/media_track_constraints_delegate.dart
index 5e34ebd3f..8c5749200 100644
--- a/lib/src/web/media_track_constraints_delegate.dart
+++ b/lib/src/web/media_track_constraints_delegate.dart
@@ -9,25 +9,28 @@ final class MediaTrackConstraintsDelegate {
/// Get the settings for the given [mediaStream].
MediaTrackSettings? getSettings(MediaStream? mediaStream) {
- final List? tracks = mediaStream?.getVideoTracks().toDart;
+ final List? tracks = mediaStream?.getVideoTracks().toDart;
if (tracks == null || tracks.isEmpty) {
return null;
}
- final MediaStreamTrack? track = tracks.first as MediaStreamTrack?;
-
- if (track == null) {
- return null;
- }
+ final MediaStreamTrack track = tracks.first;
+ final MediaTrackCapabilities capabilities = track.getCapabilities();
final MediaTrackSettings settings = track.getSettings();
+ if (capabilities.facingMode.toDart.isEmpty) {
+ return MediaTrackSettings(
+ width: settings.width,
+ height: settings.height,
+ );
+ }
+
return MediaTrackSettings(
width: settings.width,
height: settings.height,
facingMode: settings.facingMode,
- aspectRatio: settings.aspectRatio,
);
}
}
diff --git a/lib/src/web/mobile_scanner_web.dart b/lib/src/web/mobile_scanner_web.dart
index 91e2ab529..ab77313f3 100644
--- a/lib/src/web/mobile_scanner_web.dart
+++ b/lib/src/web/mobile_scanner_web.dart
@@ -25,7 +25,7 @@ class MobileScannerWeb extends MobileScannerPlatform {
String? _alternateScriptUrl;
/// The internal barcode reader.
- final BarcodeReader _barcodeReader = ZXingBarcodeReader();
+ BarcodeReader? _barcodeReader;
/// The stream controller for the barcode stream.
final StreamController _barcodesController =
@@ -35,21 +35,13 @@ class MobileScannerWeb extends MobileScannerPlatform {
StreamSubscription