Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Camera does not work after navigation from one screen to another (Both use MobileScanner) #589

Open
oleksii-tatarintsev opened this issue Apr 12, 2023 · 6 comments

Comments

@oleksii-tatarintsev
Copy link

oleksii-tatarintsev commented Apr 12, 2023

Every time when i am trying to navigate from one screen to another (both screens use MobileScanner), on the second screen i receive white screen if i do not use autostart and starting/stopping controller by myself. If i use autostart, i receive black screen

Package: mobile_scanner: 3.2.0
iOS Device: iPad Pro 12.9 inch (5th generation)
iPadOS: 16.4
Android Device: Samsung Galaxy Tab S7
Android version: Android 11

Code:

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

  @override
  State<ScannerOne> createState() => _ScannerOneState();
}

class _ScannerOneState extends State<ScannerOne> {

  final MobileScannerController controller = MobileScannerController(autoStart: false);

  @override
  void initState() {
    controller.start();
    super.initState();
  }

  @override
  void dispose() {
    controller.stop();
    controller.dispose();
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'scanner 1',
        ),
        leading: IconButton(
            onPressed: () => Modal().show(context),
            icon: Container(
              width: 50,
              height: 50,
              color: Colors.red,
            )),
      ),
      body: MobileScanner(
        controller: controller,
        onDetect: (_) {},
      ),
    );
  }
}

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

  @override
  State<ScannerTwo> createState() => _ScannerTwoState();
}

class _ScannerTwoState extends State<ScannerTwo> {

  final MobileScannerController controller = MobileScannerController(autoStart: false);

  @override
  void initState() {
    controller.start();
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'scanner 2',
        ),
        leading: IconButton(
          onPressed: () => Modal().show(context),
          icon: Container(
            width: 50,
            height: 50,
            color: Colors.red,
          ),
        ),
      ),
      body: MobileScanner(
        controller: controller,
        onDetect: (_) {},
      ),
    );
  }
}

class Modal {
  show(BuildContext context) {
    return showDialog(
      context: context,
      builder: (context) {
        return Dialog(
          child: Column(
            children: [
              CupertinoButton(
                child: const Text('Scanner 1'),
                onPressed: () {
                  Navigator.pushReplacement(
                    context,
                    MaterialPageRoute(
                      builder: (context) => const ScannerOne(),
                    ),
                  );
                },
              ),
              CupertinoButton(
                child: const Text('Scanner 2'),
                onPressed: () {
                  Navigator.pushReplacement(
                    context,
                    MaterialPageRoute(
                      builder: (context) => const ScannerTwo(),
                    ),
                  );
                },
              )
            ],
          ),
        );
      },
    );
  }
}

Console: output:

#1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:315:18)
<asynchronous suspension>
#2      EventChannel.receiveBroadcastStream.<anonymous closure> (package:flutter/src/services/platform_channel.dart:674:9)
<asynchronous suspension>

while de-activating platform stream on channel dev.steenbakker.mobile_scanner/scanner/event

flutter: mobile_scanner: not starting automatically because autoStart is set to false in the controller.
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: MobileScannerException: code genericError, message: Called start() while already started!
#0      MobileScannerController.start (package:mobile_scanner/src/mobile_scanner_controller.dart:210:7)
<asynchronous suspension>

Actual result when autostart false:
IMG_0614

Actual result when autoStart true:
IMG_0613

Flutter doctor:

[✓] Flutter (Channel stable, 3.7.7, on macOS 13.2.1 22D68 darwin-arm64, locale en-GB)
    • Flutter version 3.7.7 on channel stable at /Users/aleksey/Development/SDK/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 2ad6cd72c0 (5 weeks ago), 2023-03-08 09:41:59 -0800
    • Engine revision 1837b5be5f
    • Dart version 2.19.4
    • DevTools version 2.20.1

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    • Android SDK at /Users/aleksey/Library/Android/sdk
    • Platform android-33, build-tools 33.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.3)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14E222b
    • CocoaPods version 1.12.0

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2022.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-8887301)

[✓] IntelliJ IDEA Community Edition (version 2022.3.3)
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    • Flutter plugin version 73.0.3
    • Dart plugin version 223.8888

[✓] Connected device (3 available)
    • iPad (mobile) • 00008103-000248A82E53001E • ios            • iOS 16.4 20E5229e
    • macOS (desktop)         • macos                     • darwin-arm64   • macOS 13.2.1 22D68 darwin-arm64
    • Chrome (web)            • chrome                    • web-javascript • Google Chrome 112.0.5615.49

[✓] HTTP Host Availability
    • All required HTTP hosts are available
RPReplay_Final1681298831.mov
@iulianxpopa
Copy link

@oleksii-tatarintsev have you found any workaround? I'm in the same exact situation

@iJack93
Copy link

iJack93 commented Jun 18, 2023

Have you tried with what mentioned in this other issue? #539 (comment)
It works for me calling controller.stop() in the initState method

  @override
  void initState() {
    controller.stop(); ---> call stop here
    super.initState();
  }

@eggnogdev
Copy link

I was just running into a very similar issue and have found a workaround that works for me.

instead of defining the MobileScannerController in the Scanner class definition, pass it through as a parameter upon instantiation.

this example code may be easier to understand.

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          children: [
            TextButton(
              child: Text("BARCODE SCANNER"),
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => ScannerScreen(
                      controller: MobileScannerController(
                        autoStart: true,
                      ),
                    ),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

class ScannerScreen extends StatelessWidget {
  const ScannerScreen({
    super.key,
    required this.controller,
  });

  final MobileScannerController controller;

  @override
  Widget build(BuildContext context) {
    return MobileScanner(
      onDetect: (data) {
        controller.stop();

        Navigator.pop(context);
      },
      controller: controller,
    );
  }
}

as you can see, I create a new controller each time the ScannerScreen is navigated to, and pass it through as a parameter of ScannerScreen.

@ogdans3
Copy link

ogdans3 commented Dec 2, 2023

None of the workarounds seemed to work. What ended up working was adding a delay between stopping and starting the controller:

  @override
  void initState() {
    controller.stop();
    Future.delayed(const Duration(milliseconds: 1000), () => controller.start());
    super.initState();
  }

Not a great solution, but atleast it works. Im running this on a OnePlus Nord N100 (cheap, old phone) in debug mode. Im guessing that the timeout can be shorter on a newer phone running a production build.

@ArturDias13
Copy link

None of the workarounds seemed to work. What ended up working was adding a delay between stopping and starting the controller:

  @override
  void initState() {
    controller.stop();
    Future.delayed(const Duration(milliseconds: 1000), () => controller.start());
    super.initState();
  }

Not a great solution, but atleast it works. Im running this on a OnePlus Nord N100 (cheap, old phone) in debug mode. Im guessing that the timeout can be shorter on a newer phone running a production build.

I think you could just await the controller to stop

await controller.stop(); await controller.start();
OR

await controller.stop().whenComplete(() => controller.start());

@juliandambrosio
Copy link

juliandambrosio commented Mar 15, 2024

In my case, when scanning the QR I go to a new screen where I perform an action and make a pop to return to the QR scan.
The following code worked for me, for some reason Future.delayed is a must; otherwise it doesn't work.
Remember to set autoStart: false

final MobileScannerController mobileScannerController = MobileScannerController(autoStart: false);

@override
void initState() {
  super.initState();
  _startCamera();
}

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

  mobileScannerController.stop();
  mobileScannerController.dispose();
}

Future<void> _startCamera() async {
  Future.delayed(const Duration(seconds: 1), () async => await mobileScannerController.start());
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants