diff --git a/android/build.gradle b/android/build.gradle index e1173a39..1649317e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -26,31 +26,27 @@ apply plugin: 'kotlin-android' android { compileSdk 33 + namespace 'net.touchcapture.qr.flutterqr' // ✅ Place it here - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } defaultConfig { - // minSdkVersion is determined by Native View. minSdkVersion 20 targetSdkVersion 33 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true } - kotlinOptions { - jvmTarget = '11' + sourceSets { + main.java.srcDirs += 'src/main/kotlin' } compileOptions { - // Flag to enable support for the new language APIs - coreLibraryDesugaringEnabled true - // Sets Java compatibility to Java 11 sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 + coreLibraryDesugaringEnabled true } - if (project.android.hasProperty('namespace')) { - namespace 'net.touchcapture.qr.flutterqr' + + kotlinOptions { + jvmTarget = '11' } } @@ -59,5 +55,5 @@ dependencies { implementation('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false } implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.zxing:core:3.5.2' - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' // ✅ latest stable } diff --git a/lib/src/lifecycle_event_handler.dart b/lib/src/lifecycle_event_handler.dart index 0580d17e..deb1aaab 100644 --- a/lib/src/lifecycle_event_handler.dart +++ b/lib/src/lifecycle_event_handler.dart @@ -17,6 +17,8 @@ class LifecycleEventHandler extends WidgetsBindingObserver { case AppLifecycleState.inactive: case AppLifecycleState.paused: case AppLifecycleState.detached: + case AppLifecycleState.hidden: + break; } } } diff --git a/lib/src/web/flutter_qr_web.dart b/lib/src/web/flutter_qr_web.dart index de368ace..118b1b7f 100644 --- a/lib/src/web/flutter_qr_web.dart +++ b/lib/src/web/flutter_qr_web.dart @@ -4,8 +4,9 @@ import 'dart:async'; import 'dart:core'; import 'dart:html' as html; import 'dart:js_util'; -import 'dart:ui' as ui; +// ignore_for_file: avoid_web_libraries_in_flutter +import 'web_view_registry.dart'; import 'package:flutter/material.dart'; import '../../qr_code_scanner.dart'; @@ -24,21 +25,16 @@ class WebQrView extends StatefulWidget { final CameraFacing? cameraFacing; const WebQrView( - {Key? key, - required this.onPlatformViewCreated, - this.onPermissionSet, - this.cameraFacing = CameraFacing.front}) + {Key? key, required this.onPlatformViewCreated, this.onPermissionSet, this.cameraFacing = CameraFacing.front}) : super(key: key); @override _WebQrViewState createState() => _WebQrViewState(); - static html.DivElement vidDiv = - html.DivElement(); // need a global for the registerViewFactory + static html.DivElement vidDiv = html.DivElement(); // need a global for the registerViewFactory static Future cameraAvailable() async { - final sources = - await html.window.navigator.mediaDevices!.enumerateDevices(); + final sources = await html.window.navigator.mediaDevices!.enumerateDevices(); // List vidIds = []; var hasCam = false; for (final e in sources) { @@ -66,8 +62,7 @@ class _WebQrViewState extends State { html.VideoElement video = html.VideoElement(); String viewID = 'QRVIEW-' + DateTime.now().millisecondsSinceEpoch.toString(); - final StreamController _scanUpdateController = - StreamController(); + final StreamController _scanUpdateController = StreamController(); late CameraFacing facing; Timer? _frameIntervall; @@ -80,9 +75,14 @@ class _WebQrViewState extends State { // video = html.VideoElement(); WebQrView.vidDiv.children = [video]; - // ignore: UNDEFINED_PREFIXED_NAME - ui.platformViewRegistry - .registerViewFactory(viewID, (int id) => WebQrView.vidDiv); + // ignore: undefined_prefixed_name + platformViewRegistry.registerViewFactory( + viewID, + (int id) => WebQrView.vidDiv, + ); + + print('[QR Scanner Web] viewID: $viewID'); + // giving JavaScipt some time to process the DOM changes Timer(const Duration(milliseconds: 500), () { start(); @@ -92,8 +92,7 @@ class _WebQrViewState extends State { Future start() async { await _makeCall(); _frameIntervall?.cancel(); - _frameIntervall = - Timer.periodic(const Duration(milliseconds: 200), (timer) { + _frameIntervall = Timer.periodic(const Duration(milliseconds: 200), (timer) { _captureFrame2(); }); } @@ -137,8 +136,7 @@ class _WebQrViewState extends State { widget.onPermissionSet?.call(_controller!, true); _localStream = stream; video.srcObject = _localStream; - video.setAttribute('playsinline', - 'true'); // required to tell iOS safari we don't want fullscreen + video.setAttribute('playsinline', 'true'); // required to tell iOS safari we don't want fullscreen await video.play(); } catch (e) { cancel(); @@ -177,16 +175,14 @@ class _WebQrViewState extends State { if (_localStream == null) { return null; } - final canvas = - html.CanvasElement(width: video.videoWidth, height: video.videoHeight); + final canvas = html.CanvasElement(width: video.videoWidth, height: video.videoHeight); final ctx = canvas.context2D; // canvas.width = video.videoWidth; // canvas.height = video.videoHeight; ctx.drawImage(video, 0, 0); final imgData = ctx.getImageData(0, 0, canvas.width!, canvas.height!); - final size = - Size(canvas.width?.toDouble() ?? 0, canvas.height?.toDouble() ?? 0); + final size = Size(canvas.width?.toDouble() ?? 0, canvas.height?.toDouble() ?? 0); if (size != _size) { setState(() { _setCanvasSize(size); @@ -197,8 +193,7 @@ class _WebQrViewState extends State { final code = jsQR(imgData.data, canvas.width, canvas.height); // ignore: unnecessary_null_comparison if (code != null && code.data != null) { - _scanUpdateController - .add(Barcode(code.data, BarcodeFormat.qrcode, code.data.codeUnits)); + _scanUpdateController.add(Barcode(code.data, BarcodeFormat.qrcode, code.data.codeUnits)); } } on NoSuchMethodError { // Do nothing, this exception occurs continously in web release when no @@ -246,7 +241,7 @@ class _WebQrViewState extends State { ); } - void _setCanvasSize(ui.Size size) { + void _setCanvasSize(Size size) { setState(() { _size = size; }); @@ -263,9 +258,7 @@ class QRViewControllerWeb implements QRViewController { @override Future flipCamera() async { // TODO: improve error handling - _state.facing = _state.facing == CameraFacing.front - ? CameraFacing.back - : CameraFacing.front; + _state.facing = _state.facing == CameraFacing.front ? CameraFacing.back : CameraFacing.front; await _state.start(); return _state.facing; } @@ -325,9 +318,7 @@ class QRViewControllerWeb implements QRViewController { } } -Widget createWebQrView( - {onPlatformViewCreated, onPermissionSet, CameraFacing? cameraFacing}) => - WebQrView( +Widget createWebQrView({onPlatformViewCreated, onPermissionSet, CameraFacing? cameraFacing}) => WebQrView( onPlatformViewCreated: onPlatformViewCreated, onPermissionSet: onPermissionSet, cameraFacing: cameraFacing, diff --git a/lib/src/web/web_view_registry.dart b/lib/src/web/web_view_registry.dart new file mode 100644 index 00000000..27106cb9 --- /dev/null +++ b/lib/src/web/web_view_registry.dart @@ -0,0 +1,4 @@ +// ignore: avoid_web_libraries_in_flutter +import 'dart:ui_web' as ui_web; + +final platformViewRegistry = ui_web.platformViewRegistry; \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 72f21cb8..1ed960fd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,8 +5,8 @@ homepage: https://juliuscanute.com repository: https://github.com/juliuscanute/qr_code_scanner environment: - sdk: '>=2.17.0 <3.0.0' - flutter: ">=1.12.0" + sdk: '>=3.4.3 <4.0.0' + flutter: ">=1.17.0" dependencies: js: ^0.6.3