juliansteenbakker / mobile_scanner

A universal scanner for Flutter based on MLKit. Uses CameraX on Android and AVFoundation on iOS.
BSD 3-Clause "New" or "Revised" License
813 stars 475 forks source link

Bug : Camera permission called again if user denied first one #509

Open EArminjon opened 1 year ago

EArminjon commented 1 year ago

When user denied camera permission, system popup is called again.

camera_permission.webm

mobile_scanner : 3.0.0

Code :

import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';

void main() => runApp(const MaterialApp(home: MyHome()));

class MyHome extends StatelessWidget {
  const MyHome({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flutter Demo Home Page')),
      body: SizedBox(
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: () {
                Navigator.of(context).push(
                  MaterialPageRoute<dynamic>(
                    builder: (BuildContext context) => const BarcodeScannerWithController(),
                  ),
                );
              },
              child: const Text('MobileScanner with Controller'),
            ),
          ],
        ),
      ),
    );
  }
}

class BarcodeScannerWithController extends StatefulWidget {
  const BarcodeScannerWithController({super.key});

  @override
  _BarcodeScannerWithControllerState createState() => _BarcodeScannerWithControllerState();
}

class _BarcodeScannerWithControllerState extends State<BarcodeScannerWithController> with SingleTickerProviderStateMixin {
  BarcodeCapture? barcode;

  final MobileScannerController controller = MobileScannerController(
    torchEnabled: true,
    autoStart: true,
  );

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(controller.hashCode.toString()),
      ),
      backgroundColor: Colors.black,
      body: Stack(
        children: <Widget>[
          MobileScanner(
            startDelay: true,
            controller: controller,
            errorBuilder: (
              BuildContext context,
              MobileScannerException error,
              Widget? child,
            ) {
              return ScannerErrorWidget(error: error);
            },
            fit: BoxFit.contain,
            onDetect: (BarcodeCapture barcode) {
              setState(() {
                this.barcode = barcode;
              });
            },
          ),
        ],
      ),
    );
  }
}

class ScannerErrorWidget extends StatelessWidget {
  const ScannerErrorWidget({super.key, required this.error});

  final MobileScannerException error;

  @override
  Widget build(BuildContext context) {
    String errorMessage;

    switch (error.errorCode) {
      case MobileScannerErrorCode.controllerUninitialized:
        errorMessage = 'Controller not ready.';
        break;
      case MobileScannerErrorCode.permissionDenied:
        errorMessage = 'Permission denied';
        break;
      default:
        errorMessage = 'Generic Error';
        break;
    }

    return ColoredBox(
      color: Colors.black,
      child: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Padding(
              padding: EdgeInsets.only(bottom: 16),
              child: Icon(Icons.error, color: Colors.white),
            ),
            Text(
              errorMessage,
              style: const TextStyle(color: Colors.white),
            ),
          ],
        ),
      ),
    );
  }
}
EArminjon commented 1 year ago

Could be interesting to let us decide when call camera permission. This library can just display error when permission is missing :)

AlehSabadashGmail commented 1 year ago

I have the same, reproduced on the latest version, before updating I had 3.0.0-beta.1 and it worked fine

AlehSabadashGmail commented 1 year ago

reproduced only on android

richirisu commented 1 year ago

I have noticed the same behavior, but not only when denying but also when accepting. The system permission dialog shows up twice. However, in my case it only appears to happen on the emulator. Running the code on an actual device seems fine.

Possibly related to #419 and #407

a1573595 commented 1 year ago

I have noticed the same behavior, but not only when denying but also when accepting. The system permission dialog shows up twice. However, in my case it only appears to happen on the emulator. Running the code on an actual device seems fine.

Possibly related to #419 and #407

It will happen on a physical device.

BartholomeLB commented 1 year ago

Hello, any news about this issue, even in 3.2.0 i go this issue only on Android only on denied case ?

AlehSabadashGmail commented 1 year ago

Any updates here? current version is 3.3.0

androi7 commented 1 year ago

For me it is working on Android if I change the didChangeAppLifecycleState inside the mobile_scanner to following:

switch (state) {
      case AppLifecycleState.resumed:
        if (_resumeFromBackground)
        _startScanner();
        break;
      case AppLifecycleState.paused:
        _resumeFromBackground = true;
        break;
      case AppLifecycleState.inactive:
        _resumeFromBackground = false; // I guess this line is dead code
        _controller.stop();
        break;
      case AppLifecycleState.detached:
        break;
    } 

additional info:

first time the methodchannel invokes the request channel which opens the popup that furthermore triggers the resumed lifecycle state and therefore restarts the controller (because _resumeFromBackground has still the initial value false). when denying the permission pop up it calls the popup again. didn't check why it doesnt end in a infinite loop afterwards (maybe something is happening in the channel then)...

so the first popup triggers a resumed state. when going to background it calls first the inactive, then the paused state and in the end the resumed.

phuongnam195 commented 8 months ago

Reproduced on Android mobile_scanner 3.5.2 go with camera 0.10.5

Holofox commented 5 months ago

On Android 11 and below, the permission window will appear several times, and then begin endless attempts, which greatly slows down the application.

@juliansteenbakker