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
756 stars 446 forks source link

Scanner is active in background when Navigating to another screen #619

Closed arnabonetraker closed 2 months ago

arnabonetraker commented 1 year ago

My flow requires when barcode is detected navigate to new screen, do some activity then pop to scanner screen again. Now the issue is the scanner is active in background, so it is detecting the barcode again even when I am in a different screen.

_mobileScannerController = MobileScannerController(facing: CameraFacing.back, formats: [BarcodeFormat.code128], detectionSpeed: DetectionSpeed.normal, autoStart: true, ) This is my controller settings. I have tried stoping the camera using stop() before navigating to another screen. then using callback to start() again when it is poped. This works for android but it is not working in a IOS device. For IOS the scanner is still activate in the background.


Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.7.12, on macOS 13.3.1 22E772610a darwin-x64, locale en-IN)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 14.3)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2022.2)
[✓] Connected device (3 available)
[✓] HTTP Host Availability
teolemon commented 1 year ago
arnabonetraker commented 1 year ago

Not quite. In my case scanner keep detecting barcode even while navigating to other screen(no scanner initialised in this second screen).

denihero commented 1 year ago

In my case after navigation I do cameraController.stop(), but when I return to scan page camera is not working.

arnabonetraker commented 1 year ago

In my case after navigation I do cameraController.stop(), but when I return to scan page camera is not working.

did you set your MobileScannerController(autoStart:true) ? If not then you will have to cameraController.start() when you pop and get the callback.

denihero commented 1 year ago

In my case after navigation I do cameraController.stop(), but when I return to scan page camera is not working.

did you set your MobileScannerController(autoStart:true) ? If not then you will have to cameraController.start() when you pop and get the callback.

autoStart:true stay automaticly, I tried to pass controller to the new Screen and call cameraController.start(), but it's not working

JoseSarricolea commented 1 year ago

@denihero I think the problem is in the controller. I managed, Overwriting the controller every time it wants to scan again. Try this:

Declare the controller so you don't have to check for null in functions: MobileScannerController cameraController = MobileScannerController();

When I want to stop the scanner:

FutureOr<void> _onQrViewCameraStopped( QrViewCameraStopped event, Emitter<QrViewState> emit) { cameraController.stop(); cameraController.dispose(); }

When I want to scan a qr: FutureOr<void> _onQrViewLoaded( QrViewLoaded event, Emitter<QrViewState> emit) { cameraController = MobileScannerController(); emit(QrViewLoad(event.plants)); }

denihero commented 1 year ago

@denihero I think the problem is in the controller. I managed, Overwriting the controller every time it wants to scan again. Try this:

Declare the controller so you don't have to check for null in functions: MobileScannerController cameraController = MobileScannerController();

When I want to stop the scanner:

FutureOr<void> _onQrViewCameraStopped( QrViewCameraStopped event, Emitter<QrViewState> emit) { cameraController.stop(); cameraController.dispose(); }

When I want to scan a qr: FutureOr<void> _onQrViewLoaded( QrViewLoaded event, Emitter<QrViewState> emit) { cameraController = MobileScannerController(); emit(QrViewLoad(event.plants)); }

Where did you get QrViewLoadedand QrViewState?

JoseSarricolea commented 1 year ago

Am using Bloc on my proyect

braveshine1993m commented 1 year ago

In case people still dealing with this issue, you can do like below :

controller.stop();
Navigator.pushNamed(context, 'someRoute').then((value) {
    controller.start();
});

if this wasn't enough, do : Navigator.pop(context, true) in second page. No need to bloc or etc.

denihero commented 1 year ago

thanks, man, it's working, I'll appreciate it

michael11albrecht commented 9 months ago

So as I see at the moment there is no option to pop back from the Scanner page and later use the Scanner again? I tried different approaches, stopping and starting the controller manually or completely dispose the controller. But I couldn't find a way getting the Scanner run again after leaving it ones (except leaving it forward (push) and afterwards going back like @braveshine1993m recommendet. I tested on an iPhone 12 ios 16.5.1 and the android emulator.

Edit: For me this approch lets me use the scanner how i need it: [https://github.com/juliansteenbakker/mobile_scanner/issues/589#issuecomment-1599677867]

manishvill commented 4 months ago

Note: This is just a temporary solution

If you are using flutter bloc, then you can use that to solve this issue. Create a Cubit for Scanner View, and add controller in Cubit.

class ScannerCubit extends Cubit<ScannerState> {
  ScannerCubit() : super(ScannerInitial());
  MobileScannerController controller =
      MobileScannerController(autoStart: false);
  late bool _killCamera = true;

  Future<void> start() async {
    _killCamera = false;
    if (!controller.isStarting) {
      await controller.start();
    }
    controller.startArguments.addListener(_cameraEvent);
  }

  void _cameraEvent() async {
    if (_killCamera) {
      await controller.stop();
      controller.dispose();
    }
  }

  Future<void> stop() async {
    _killCamera = true;
    await controller.stop();
    controller.dispose();
  }
}

How to use in View

class _ScannerState extends State<Scanner> {

  @override
  void initState() {
    WidgetsFlutterBinding.ensureInitialized();
    BlocProvider.of<ScannerCubit>(context).start();
    super.initState();
  }

  @override
  void dispose() {
    BlocProvider.of<QrScannerCubit>(context).stop();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Scanner"),
      ),
      body: MobileScanner(
        controller: BlocProvider.of<QrScannerCubit>(context).controller,
        onDetect: (value) {},
      ),
    );
  }
}

Call dartBlocProvider.of<QrScannerCubit>(context).stop() in other Views, it will stop any active camera instance. for example

class _HomeState extends State<Home> {

  @override
  void initState() {
    BlocProvider.of<QrScannerCubit>(context).stop();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text("HOME"),
    );
  }
}
navaronbracke commented 2 months ago

With the new beta release, version 5.0.0-beta.1, the lifecycle of the MobileScannerController is now user managed.

So I'm going to close this issue.