From f74c3e5aa502cad7f44425f27c00db8531cef7fb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 11 Aug 2025 14:17:46 +0000 Subject: [PATCH] Replace mobile_scanner with qr_code_scanner_plus for QR code scanning Co-authored-by: charl --- .../lib/src/input/qr_code_scanner.dart | 138 ++++++++---------- packages/komodo_ui/pubspec.yaml | 2 +- 2 files changed, 58 insertions(+), 82 deletions(-) diff --git a/packages/komodo_ui/lib/src/input/qr_code_scanner.dart b/packages/komodo_ui/lib/src/input/qr_code_scanner.dart index ce351fc7..3e8db75b 100644 --- a/packages/komodo_ui/lib/src/input/qr_code_scanner.dart +++ b/packages/komodo_ui/lib/src/input/qr_code_scanner.dart @@ -1,6 +1,6 @@ // import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; class QrCodeReaderOverlay extends StatefulWidget { const QrCodeReaderOverlay({super.key}); @@ -19,7 +19,16 @@ class QrCodeReaderOverlay extends StatefulWidget { } class _QrCodeReaderOverlayState extends State { - bool qrDetected = false; + final GlobalKey debugKey = GlobalKey(debugLabel: 'QR'); + QRViewController? _controller; + bool _qrDetected = false; + bool _controllerReady = false; + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -30,93 +39,60 @@ class _QrCodeReaderOverlayState extends State { foregroundColor: Theme.of(context).textTheme.bodyMedium?.color, elevation: 0, ), - body: MobileScanner( - controller: MobileScannerController( - detectionTimeoutMs: 1000, - formats: [BarcodeFormat.qrCode], - ), - errorBuilder: _buildQrScannerError, - onDetect: (capture) { - if (qrDetected) return; - - final qrCodes = capture.barcodes; - - if (qrCodes.isNotEmpty) { - final r = qrCodes.first.rawValue; - qrDetected = true; - - // MRC: Guarantee that we don't try to close the current screen - // if it was already closed - if (!context.mounted) return; - Navigator.pop(context, r); - } - }, - placeholderBuilder: - (context) => const Center(child: CircularProgressIndicator()), + body: Stack( + children: [ + QRView( + key: debugKey, + onQRViewCreated: _onQRViewCreated, + formatsAllowed: const [BarcodeFormat.qrcode], + overlay: QrScannerOverlayShape( + borderColor: Theme.of(context).colorScheme.primary, + borderWidth: 8, + borderRadius: 12, + borderLength: 32, + cutOutSize: MediaQuery.of(context).size.width * 0.7, + ), + onPermissionSet: (ctrl, granted) { + if (!granted) { + _showPermissionDenied(context); + } + }, + ), + if (!_controllerReady) + const Center(child: CircularProgressIndicator()), + ], ), ); } - Widget _buildQrScannerError( - BuildContext context, - MobileScannerException exception, - ) { - late String errorMessage; + void _onQRViewCreated(QRViewController controller) { + _controller = controller; + setState(() => _controllerReady = true); - switch (exception.errorCode) { - case MobileScannerErrorCode.controllerUninitialized: - errorMessage = // TODO!l10n: LocaleKeys.qrScannerErrorControllerUninitialized.tr(); - 'The controller was used before being initialized'; + controller.scannedDataStream.listen((barcode) { + if (_qrDetected) return; + final String? code = barcode.code; + if (code == null || code.isEmpty) return; - case MobileScannerErrorCode.permissionDenied: - errorMessage = // TODO!l10n: LocaleKeys.qrScannerErrorPermissionDenied.tr(); - 'Permission to use the camera was denied. Please enable camera ' - 'permissions in your device settings.'; - // TODO: Disable the scanner button if the device does not - // support scanning - case MobileScannerErrorCode.unsupported: - errorMessage = //TODO!l10n: LocaleKeys.qrScannerErrorUnsupported.tr(); - 'Your device does not support scanning QR codes.'; - case MobileScannerErrorCode.controllerNotAttached: - case MobileScannerErrorCode.genericError: - case MobileScannerErrorCode.controllerDisposed: - case MobileScannerErrorCode.controllerAlreadyInitialized: - case MobileScannerErrorCode.controllerInitializing: - errorMessage = //TODO!l10n: LocaleKeys.qrScannerErrorGenericError.tr(); - 'An error occurred while scanning the QR code. Please try again.'; - } + _qrDetected = true; + if (!mounted) return; + Navigator.pop(context, code); + }); + } - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.warning, color: Colors.yellowAccent, size: 64), - const SizedBox(height: 8), - Text( - //TODO!l10n: LocaleKeys.qrScannerErrorTitle.tr(), - 'Error', - style: Theme.of(context).textTheme.titleLarge, + void _showPermissionDenied(BuildContext context) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Error'), + content: const Text( + 'Permission to use the camera was denied. Please enable camera permissions in your device settings.', + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK'), ), - const SizedBox(height: 32), - Text(errorMessage, style: Theme.of(context).textTheme.bodyLarge), - if (exception.errorDetails != null) - Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - //TODO!l10n: '${LocaleKeys.errorCode.tr()}: ${exception.errorDetails!.code}', - 'Error code: ${exception.errorDetails!.code}', - ), - Text( - //TODO!l10n: '${LocaleKeys.errorDetails.tr()}: ${exception.errorDetails!.details}', - 'Error details: ${exception.errorDetails!.details}', - ), - Text( - //TODO!l10n: '${LocaleKeys.errorMessage.tr()}: ${exception.errorDetails!.message}', - 'Error message: ${exception.errorDetails!.message}', - ), - ], - ), ], ), ); diff --git a/packages/komodo_ui/pubspec.yaml b/packages/komodo_ui/pubspec.yaml index 6584b47d..7f5797a2 100644 --- a/packages/komodo_ui/pubspec.yaml +++ b/packages/komodo_ui/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: intl: ^0.20.2 komodo_defi_types: path: ../komodo_defi_types - mobile_scanner: ^7.0.0 + qr_code_scanner_plus: ^2.0.10+1 dev_dependencies: flutter_test: