From 2bddce60c4852eac55596b4bf40746cfba5dab20 Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 12 Jul 2024 04:32:21 -0700 Subject: [PATCH] Fixed Camera permission handling for QR --- .../common/qr_code_scanner_screen.dart | 228 +++++++++++++++--- 1 file changed, 190 insertions(+), 38 deletions(-) diff --git a/lib/features/common/qr_code_scanner_screen.dart b/lib/features/common/qr_code_scanner_screen.dart index 450f9838..4e18ae97 100644 --- a/lib/features/common/qr_code_scanner_screen.dart +++ b/lib/features/common/qr_code_scanner_screen.dart @@ -1,3 +1,6 @@ +import 'dart:async'; +import 'dart:developer'; + import 'package:dartx/dartx.dart'; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; @@ -28,62 +31,185 @@ class QRCodeScannerScreen extends StatefulHookConsumerWidget { } class _QRCodeScannerScreenState extends ConsumerState with WidgetsBindingObserver, PresLogger { - final controller = MobileScannerController(detectionTimeoutMs: 500, autoStart: false); + final MobileScannerController controller = MobileScannerController( + detectionTimeoutMs: 500, + // autoStart: false, + ); bool started = false; - late FlutterEasyPermission _easyPermission; + // late FlutterEasyPermission _easyPermission; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); + _initializeScanner(); - _easyPermission = FlutterEasyPermission() - ..addPermissionCallback(onGranted: (requestCode, androidPerms, iosPerm) { - debugPrint("android:$androidPerms"); - debugPrint("iOS:$iosPerm"); - startQrScannerIfPermissionGranted(); - }, onDenied: (requestCode, androidPerms, iosPerm, isPermanent) { - if (isPermanent) { - FlutterEasyPermission.showAppSettingsDialog(title: "Camera"); - } else { - debugPrint("android:$androidPerms"); - debugPrint("iOS:$iosPerm"); - } - }, onSettingsReturned: () { - startQrScannerIfPermissionGranted(); - }); + // _easyPermission = FlutterEasyPermission() + // ..addPermissionCallback(onGranted: (requestCode, androidPerms, iosPerm) { + // debugPrint("android:$androidPerms"); + // debugPrint("iOS:$iosPerm"); + // startQrScannerIfPermissionGranted(); + // }, onDenied: (requestCode, androidPerms, iosPerm, isPermanent) { + // if (isPermanent) { + // FlutterEasyPermission.showAppSettingsDialog(title: "Camera"); + // } else { + // debugPrint("android:$androidPerms"); + // debugPrint("iOS:$iosPerm"); + // } + // }, onSettingsReturned: () { + // startQrScannerIfPermissionGranted(); + // }); + } + + Future _requestCameraPermission() async { + final hasPermission = await FlutterEasyPermission.has( + perms: permissions, + permsGroup: permissionGroup, + ); + + if (hasPermission) return true; + + final completer = Completer(); + + void permissionCallback(int requestCode, List ?perms, PermissionGroup ?perm) { + if (!completer.isCompleted) { + completer.complete(true); + } + } + + void permissionDeniedCallback(int requestCode, List ?perms, PermissionGroup ?perm, bool isPermanent) { + if (!completer.isCompleted) { + completer.complete(false); + } + } + + FlutterEasyPermission().addPermissionCallback( + onGranted: permissionCallback, + onDenied: permissionDeniedCallback, + ); + + FlutterEasyPermission.request( + perms: permissions, + permsGroup: permissionGroup, + rationale: "Camera permission is required to scan QR codes.", + ); + + return completer.future; + } + + Future _initializeScanner() async { + final hasPermission = await _requestCameraPermission(); + if (hasPermission) { + _startScanner(); + } else { + _showPermissionDialog(); + } } @override void dispose() { - controller.stop(); - _easyPermission.dispose(); + controller.dispose(); + // _easyPermission.dispose(); + FlutterEasyPermission().dispose(); WidgetsBinding.instance.removeObserver(this); super.dispose(); } - void startQrScannerIfPermissionGranted() { - FlutterEasyPermission.has(perms: permissions, permsGroup: permissionGroup).then((value) { - if (value) { - controller.start().then((result) { - if (result != null) { - setState(() { - started = true; - }); - } - }).catchError((error) { - loggy.warning("Error starting scanner: $error"); - }); - } else {} + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + /// Checking app cycle so that when user returns from settings, need to recheck for permissions + if (state == AppLifecycleState.resumed) { + _checkPermissionAndStartScanner(); + } + } + + Future _checkPermissionAndStartScanner() async { + final hasPermission = await FlutterEasyPermission.has( + perms: permissions, + permsGroup: permissionGroup, + ); + if (hasPermission) { + _startScanner(); + } else { + setState(() {}); // Trigger rebuild to show permission denied UI + } + } + + Future _startScanner() async { + await controller.start().whenComplete(() { + setState(() { + started = true; + }); + }).catchError((error) { + loggy.warning("Error starting scanner: $error"); }); } + Future startQrScannerIfPermissionIsGranted() async { + final hasPermission = await FlutterEasyPermission.has( + perms: permissions, + permsGroup: permissionGroup, + ); + if (hasPermission) { + _startScanner(); + } else { + _showPermissionDialog(); + } + } + + // void startQrScannerIfPermissionGranted() { + // FlutterEasyPermission.has(perms: permissions, permsGroup: permissionGroup).then((value) { + // if (value) { + // controller.start().then((result) { + // if (result != null) { + // setState(() { + // started = true; + // }); + // } + // }).catchError((error) { + // loggy.warning("Error starting scanner: $error"); + // }); + // } else {} + // }); + // } + + void _showPermissionDialog() { + FlutterEasyPermission.showAppSettingsDialog( + title: "Camera Access Required", + rationale: "Permission to camera to scan QR Code", + positiveButtonText: "Settings", + negativeButtonText: "Cancel", + ); + } + @override Widget build(BuildContext context) { - final t = ref.watch(translationsProvider); + final Translations t = ref.watch(translationsProvider); - startQrScannerIfPermissionGranted(); + // startQrScannerIfPermissionGranted(); + return FutureBuilder( + future: FlutterEasyPermission.has( + perms: permissions, + permsGroup: permissionGroup, + ), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + + if (snapshot.data == true) { + return _buildScannerUI(context, t); + } else { + return _buildPermissionDeniedUI(context, t); + } + }, + ); + } + + + + Widget _buildScannerUI(BuildContext context, Translations t) { final size = MediaQuery.sizeOf(context); final overlaySize = (size.shortestSide - 12).coerceAtMost(248); @@ -92,9 +218,9 @@ class _QRCodeScannerScreenState extends ConsumerState with appBar: AppBar( backgroundColor: Colors.transparent, iconTheme: Theme.of(context).iconTheme.copyWith( - color: Colors.white, - size: 32, - ), + color: Colors.white, + size: 32, + ), actions: [ IconButton( icon: ValueListenableBuilder( @@ -179,6 +305,32 @@ class _QRCodeScannerScreenState extends ConsumerState with ), ); } + + Widget _buildPermissionDeniedUI(BuildContext context, Translations t) { + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + iconTheme: Theme.of(context).iconTheme.copyWith( + color: Colors.white, + size: 32, + ), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(t.profile.add.qrScanner.permissionDeniedError), + const SizedBox(height: 16), + ElevatedButton( + onPressed: _showPermissionDialog, + child: const Text("Settings"), + ), + ], + ), + ), + ); + } + } class ScannerOverlay extends CustomPainter { @@ -233,4 +385,4 @@ class ScannerOverlay extends CustomPainter { bool shouldRepaint(covariant CustomPainter oldDelegate) { return false; } -} +} \ No newline at end of file