khoren93 / flutter_zxing

Flutter plugin for scanning and generating QR codes using the ZXing library, supporting Android, iOS, and desktop platforms
https://pub.dev/packages/flutter_zxing
MIT License
98 stars 56 forks source link

onScan is called again when ReaderWidget is shown after stopImageStream() is used #71

Closed kaboc closed 1 year ago

kaboc commented 1 year ago

Below is an example app to demonstrate the issue.

  1. The camera preview is shown.
  2. If a barcode / QR code is scanned, the onScan callback is called. In the callback:
    • stopImageStream() is called.
    • The scanned text is assigned to _text.
  3. The camera preview is removed and the result view is shown.
  4. If the "Back to camera" button is pressed, the camera preview is shown.
  5. onScan is called immediately and the result view is shown. (This is wrong!)
  6. If the "Back to camera" button is pressed, the camera preview is shown.
  7. onScan is not called this time.
import 'package:flutter/material.dart';

import 'package:camera/camera.dart';
import 'package:flutter_zxing/flutter_zxing.dart';

void main() {
  runApp(const App());
}

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

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  String? _text;
  CameraController? _controller;

  @override
  Widget build(BuildContext context) {
    final text = _text;
    final scanned = text != null && text.isNotEmpty;

    return MaterialApp(
      home: Scaffold(
        body: scanned
            ? Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(text),
                    ElevatedButton(
                      onPressed: () => setState(() => _text = null),
                      child: const Text('Back to camera'),
                    ),
                  ],
                ),
              )
            : ReaderWidget(
                onControllerCreated: (controller) {
                  _controller = controller;
                },
                onScan: (code) async {
                  await _controller?.stopImageStream(); // This causes the issue.
                  setState(() => _text = code.text);
                },
              ),
      ),
    );
  }
}

I used stopImageStream() because I had experienced duplicate calls to the scan callback in another plugin. I'm not sure if it can happen with flutter_zxing too, but I wanted to make sure onScan() was called only once.

I don't think it is a critical issue, but it took me hours to figure out that stopImageStream() was causing it, and I still have no idea why it is wrong to use it there. I feel that something is wrong in the package and it is better that the issue is fixed so that other developers won't experience it.

kaboc commented 1 year ago

Here is a log that was printed in the console when a QR code was scanned.

I/flutter ( 4417): onScan
I/Camera  ( 4417): startPreview
I/Camera  ( 4417): CameraCaptureSession onConfigured
I/Camera  ( 4417): Updating builder settings
D/Camera  ( 4417): Updating builder with feature: ExposureLockFeature
D/Camera  ( 4417): Updating builder with feature: ExposurePointFeature
D/Camera  ( 4417): Updating builder with feature: ZoomLevelFeature
D/Camera  ( 4417): Updating builder with feature: AutoFocusFeature
D/Camera  ( 4417): Updating builder with feature: NoiseReductionFeature
I/Camera  ( 4417): updateNoiseReduction | currentSetting: fast
D/Camera  ( 4417): Updating builder with feature: FocusPointFeature
D/Camera  ( 4417): Updating builder with feature: ResolutionFeature
D/Camera  ( 4417): Updating builder with feature: SensorOrientationFeature
D/Camera  ( 4417): Updating builder with feature: FlashFeature
D/Camera  ( 4417): Updating builder with feature: ExposureOffsetFeature
D/Camera  ( 4417): Updating builder with feature: FpsRangeFeature
I/Camera  ( 4417): refreshPreviewCaptureSession
I/Camera  ( 4417): CameraCaptureSession onClosed
I/Camera  ( 4417): dispose
I/Camera  ( 4417): close
I/Camera  ( 4417): open | onClosed

The first line is a print call I added before stopImageStream() in the onScan callback.

onScan: (code) async {
  print('onScan');
  await _controller?.stopImageStream();
  setState(() => _text = code.text);
},

It seems strange that "startPreview" appears when the image stream is stopped.

khoren93 commented 1 year ago

Thank you for bringing these issues to my attention. I really appreciate the time you took to report this and for helping me improve. I'll be sure to take a look and get back to you as soon as possible.

Best regards, Khoren

khoren93 commented 1 year ago

Hi there,

I just wanted to let you know that I have updated the example in the repository. Could you please take a look and see if everything is working as expected?

Thank you!

kaboc commented 1 year ago

@khoren93 I'm sorry if my explanation was unclear. I actually don't know if there was anything wrong in the example of the package itself. The issue is about the strange behaviour that the scan result remains and triggers the onScan callback immediately next time if stopImageStream() is used when scanning is successful. I didn't mean the example in the repository should be improved, but it is great that the example is better now.

khoren93 commented 1 year ago

Hi @kaboc In my case, I have not experienced duplicate calls with the scan callback when using flutter_zxing, so I would not recommend using stopImageStream().