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
89 stars 50 forks source link

Removing ReaderWidget before it becomes ready causes error #73

Open kaboc opened 1 year ago

kaboc commented 1 year ago
import 'package:flutter/material.dart';
import 'package:flutter_zxing/flutter_zxing.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Builder(
        builder: (context) => Scaffold(
          body: Center(
            child: ElevatedButton(
              onPressed: () => showDialog(
                context: context,
                builder: (_) => const _Dialog(),
              ),
              child: const Text('Open scan dialog'),
            ),
          ),
        ),
      ),
    );
  }
}

class _Dialog extends StatelessWidget {
  const _Dialog();

  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      content: AspectRatio(
        aspectRatio: 3 / 4,
        child: ReaderWidget(onScan: (_) {}),
      ),
      actions: [
        ElevatedButton(
          onPressed: () => Navigator.of(context).pop(),
          child: const Text('Close'),
        ),
      ],
    );
  }
}

Steps to reproduce:

  1. Tap on the "Open scan dialog" button.
  2. It opens a dialog with ReaderWidget.
  3. Close the dialog before the camera preview appears.
  4. See the output in the console.

It gives different errors depending on how quickly the dialog is closed.

a) If it is very quick:

I/flutter (16061): Error: A CameraController was used after being disposed.
I/flutter (16061): Once you have called dispose() on a CameraController, it can no longer be used.
E/flutter (16061): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: A CameraController was used after being disposed.
E/flutter (16061): Once you have called dispose() on a CameraController, it can no longer be used.
E/flutter (16061): #0      ChangeNotifier.debugAssertNotDisposed.<anonymous closure> (package:flutter/src/foundation/change_notifier.dart:147:9)
E/flutter (16061): #1      ChangeNotifier.debugAssertNotDisposed (package:flutter/src/foundation/change_notifier.dart:154:6)
E/flutter (16061): #2      ChangeNotifier.addListener (package:flutter/src/foundation/change_notifier.dart:207:27)
E/flutter (16061): #3      _ReaderWidgetState.onNewCameraSelected (package:flutter_zxing/src/ui/reader_widget.dart:158:17)

b) If it is a little later:

======== Exception caught by services library ======================================================
The following PlatformException was thrown while activating platform stream on channel plugins.flutter.io/camera_android/imageStream:
PlatformException(error, Attempt to invoke virtual method 'void android.media.ImageReader.setOnImageAvailableListener(android.media.ImageReader$OnImageAvailableListener, android.os.Handler)' on a null object reference, null, null)

When the exception was thrown, this was the stack: 
#0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:653:7)
#1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:296:18)
<asynchronous suspension>
#2      EventChannel.receiveBroadcastStream.<anonymous closure> (package:flutter/src/services/platform_channel.dart:637:9)
<asynchronous suspension>
====================================================================================================
kaboc commented 1 year ago

Regarding the first case, I think the non-null assertion operator (!) should be avoided when methods are called on the CameraController. Even if it is checked before the try block that the controller is not null, it is possible that it turns to null while futures are awaited. It is safer to use ? instead.

https://github.com/khoren93/flutter_zxing/blob/9c74b7ab51d21a5cf930969abe817d05c957249a/lib/src/ui/reader_widget.dart#L143-L160

khoren93 commented 1 year ago

Hi there!

It looks like this issue has been fixed in the main branch. Please check to see if the issue has been resolved for you and let me know if you have any further issues or questions.

Thanks for bringing this to my attention!

kaboc commented 1 year ago

@khoren93 Hi, thanks for quickly looking into the issue.

I ran the same code with the latest commit in the main branch.

a) The issue still seems to be around, although the output is different now. It has the first two lines without stack trace.

I/flutter (22532): Error: A CameraController was used after being disposed. I/flutter (22532): Once you have called dispose() on a CameraController, it can no longer be used.

b) The app raises the same error.