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][ios] : Switching between front and back camera throws CameraException(Disposed CameraController, buildPreview() was called on a disposed CameraController.) exception. #90116

Open
carman247 opened this issue Sep 15, 2021 · 15 comments
Labels
found in release: 2.5 Found to occur in 2.5 found in release: 2.6 Found to occur in 2.6 has reproducible steps The issue has been confirmed reproducible and is ready to work on p: camera The camera plugin P2 Important issues not at the top of the work list package flutter/packages repository. See also p: labels. platform-ios iOS applications specifically team-ios Owned by iOS platform team triaged-ios Triaged by iOS platform team

Comments

@carman247
Copy link

carman247 commented Sep 15, 2021

I don't know how this is happening yet, and it doesn't seem to be a major problem, but for some reason I'm getting reports of "buildPreview() was called on a disposed CameraController"

CameraException(Disposed CameraController, buildPreview() was called on a disposed CameraController.).

I can see there is a "debugCheckIsDisposed" function, which returns a void, but maybe it would be useful to either have that return a bool or set "isInitialised" to false after dispose has been called.

I'm using camera 0.9.2+2 and it's basically the same setup as the example.

log
0
camera_controller.dart - Line 821
_throwIfNotInitialized + 821
1
camera_controller.dart - Line 575
buildPreview + 575
2
camera_preview.dart - Line 34
build.<fn> + 34
3
value_listenable_builder.dart - Line 187
build + 187
4
framework.dart - Line 4691
build + 4691
5
framework.dart - Line 4574
performRebuild + 4574
6
framework.dart - Line 4746
performRebuild + 4746
7
framework.dart - Line 4267
rebuild + 4267
8
framework.dart - Line 4778
update + 4778
9
framework.dart - Line 3350
updateChild + 3350
10
framework.dart - Line 4599
performRebuild + 4599
11
framework.dart - Line 4267
rebuild + 4267
12
framework.dart - Line 4655
update + 4655
13
framework.dart - Line 3350
updateChild + 3350
14
framework.dart - Line 6090
update + 6090
15
framework.dart - Line 3350
updateChild + 3350
16
framework.dart - Line 6090
update + 6090
17
framework.dart - Line 3350
updateChild + 3350
18
framework.dart - Line 5596
updateChildren + 5596
19
framework.dart - Line 6243
update + 6243
20
framework.dart - Line 3350
updateChild + 3350
21
framework.dart - Line 4599
performRebuild + 4599
22
framework.dart - Line 4267
rebuild + 4267
23
framework.dart - Line 4655
update + 4655
24
framework.dart - Line 3350
updateChild + 3350
25
framework.dart - Line 4599
performRebuild + 4599
26
framework.dart - Line 4267
rebuild + 4267
27
framework.dart - Line 4922
update + 4922
28
framework.dart - Line 3350
updateChild + 3350
29
framework.dart - Line 4599
performRebuild + 4599
30
framework.dart - Line 4267
rebuild + 4267
31
framework.dart - Line 4922
update + 4922
32
framework.dart - Line 3350
updateChild + 3350
33
framework.dart - Line 5596
updateChildren + 5596
34
framework.dart - Line 6243
update + 6243
35
framework.dart - Line 3350
updateChild + 3350
36
framework.dart - Line 4599
performRebuild + 4599
37
framework.dart - Line 4746
performRebuild + 4746
38
framework.dart - Line 4267
rebuild + 4267
39
framework.dart - Line 4778
update + 4778
40
framework.dart - Line 3350
updateChild + 3350
41
framework.dart - Line 4599
performRebuild + 4599
42
framework.dart - Line 4267
rebuild + 4267
43
framework.dart - Line 4922
update + 4922
44
framework.dart - Line 3350
updateChild + 3350
45
framework.dart - Line 4599
performRebuild + 4599
46
framework.dart - Line 4746
performRebuild + 4746
47
framework.dart - Line 4267
rebuild + 4267
48
framework.dart - Line 4778
update + 4778
49
framework.dart - Line 3350
updateChild + 3350
50
framework.dart - Line 6090
update + 6090
51
framework.dart - Line 3350
updateChild + 3350
52
framework.dart - Line 4599
performRebuild + 4599
53
framework.dart - Line 4267
rebuild + 4267
54
framework.dart - Line 4655
update + 4655
55
framework.dart - Line 3350
updateChild + 3350
56
framework.dart - Line 6090
update + 6090
57
framework.dart - Line 3350
updateChild + 3350
58
framework.dart - Line 4599
performRebuild + 4599
59
framework.dart - Line 4746
performRebuild + 4746
60
framework.dart - Line 4267
rebuild + 4267
61
framework.dart - Line 4778
update + 4778
62
framework.dart - Line 3350
updateChild + 3350
63
framework.dart - Line 4599
performRebuild + 4599
64
framework.dart - Line 4746
performRebuild + 4746
65
framework.dart - Line 4267
rebuild + 4267
66
framework.dart - Line 4778
update + 4778
67
framework.dart - Line 3350
updateChild + 3350
68
framework.dart - Line 4599
performRebuild + 4599
69
framework.dart - Line 4267
rebuild + 4267
70
framework.dart - Line 4922
update + 4922
71
framework.dart - Line 3350
updateChild + 3350
72
framework.dart - Line 4599
performRebuild + 4599
73
framework.dart - Line 4746
performRebuild + 4746
74
framework.dart - Line 4267
rebuild + 4267
75
framework.dart - Line 4778
update + 4778
76
framework.dart - Line 3350
updateChild + 3350
77
framework.dart - Line 4599
performRebuild + 4599
78
framework.dart - Line 4746
performRebuild + 4746
79
framework.dart - Line 4267
rebuild + 4267
80
framework.dart - Line 2582
buildScope + 2582
81
binding.dart - Line 875
drawFrame + 875
82
binding.dart - Line 328
_handlePersistentFrameCallback + 328
83
binding.dart - Line 1144
_invokeFrameCallback + 1144
84
binding.dart - Line 1082
handleDrawFrame + 1082
85
binding.dart - Line 998
_handleDrawFrame + 998







@darshankawar darshankawar added the in triage Presently being triaged by the triage team label Sep 16, 2021
@darshankawar
Copy link
Member

@carman247
There's a similar open issue reporting about same message, #81816
Please check that and see if your case is similar to it.

@darshankawar darshankawar added the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Sep 16, 2021
@carman247
Copy link
Author

@darshankawar, it could be the same issue. So far I'm unable to reproduce without forcing it.

It hasn't occurred on any Android devices yet, only iOS ... Also, it seems like it's only occurring since upgrading to 0.9.2+2

@github-actions github-actions bot removed the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Sep 16, 2021
@darshankawar
Copy link
Member

@carman247 Thanks for the update.
Would be great if you could identify a pattern that leads to the message, so that it'll help to identify the issue.

@darshankawar darshankawar added the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Sep 16, 2021
@carman247
Copy link
Author

carman247 commented Sep 22, 2021

@darshankawar This definitely seems to have something to do with didChangeAppLifecycleState


1
05:49:50.807 pm
Instance of 'DiagnosticsDebugCreator'
2
05:49:50.512 pm
camera_did_app_change_lifecycle
3
05:48:10.756 pm
camera_init_controller
4
05:48:10.749 pm
camera_did_app_change_lifecycle
5
05:48:08.640 pm
camera_did_app_change_lifecycle
6
05:48:08.011 pm
camera_did_app_change_lifecycle
7
05:48:07.498 pm
camera_take_picture
8
05:47:36.956 pm
camera_init_controller
9
05:47:36.942 pm
camera_permissions_granted
10
05:47:36.934 pm
camera_get_permission_status
11
05:47:36.927 pm
camera_selected_camera
12
05:47:36.918 pm
camera_get_cameras
13
05:47:32.939 pm
camera_take_picture
14
05:47:20.775 pm
camera_init_controller
15
05:47:20.767 pm
camera_permissions_granted
16
05:47:20.761 pm
camera_get_permission_status
17
05:47:20.756 pm
camera_selected_camera
18
05:47:20.748 pm
camera_get_cameras
19
05:47:07.151 pm
camera_take_picture
20
05:47:02.051 pm
camera_did_app_change_lifecycle
21
05:47:02.044 pm
camera_init_controller
22
05:47:02.034 pm
camera_permissions_granted
23
05:47:00.263 pm
camera_did_app_change_lifecycle
24
05:47:00.239 pm
camera_did_app_change_lifecycle
25
05:46:56.174 pm
camera_did_app_change_lifecycle
26
05:46:56.154 pm
camera_requesting_permissions
27
05:46:56.137 pm
camera_permissions_not_granted
28
05:46:56.110 pm
camera_get_permission_status
29
05:46:56.104 pm
camera_selected_camera
30
05:46:56.083 pm
camera_get_cameras

This is how mine is setup, basically the same as the camera example.

  void didChangeAppLifecycleState(AppLifecycleState state) {
    GA.analytics.logEvent(name: 'camera_did_app_change_lifecycle', parameters: {'appLifecycleState': state.toString()});
    final CameraController cameraController = _cameraController;
    if (cameraController == null || !cameraController.value.isInitialized) {
      return;
    }
    if (state == AppLifecycleState.inactive) {
      cameraController.dispose();
    } else if (state == AppLifecycleState.resumed) {
      _initController();
    }
  }
  void _initController() async {
    try {
      GA.analytics.logEvent(name: 'camera_init_controller');
      if (_cameraController != null) {
        await _cameraController.dispose();
      }
      final CameraController cameraController = CameraController(
        _cameras[_selectedCameraIndex],
        ResolutionPreset.high,
      );
      _cameraController = cameraController;
      _cameraController.addListener(() {
        if (mounted) setState(() {});
        if (_cameraController.value.hasError) {
          FirebaseCrashlytics.instance.recordError(_cameraController.value.errorDescription, null);
          Utils.showErrorAlert(context, _l10n.error, _l10n.somethingWentWrong, () {
            Navigator.pop(context);
          });
        }
      });
      await _cameraController.initialize();
      await _lockCaptureOrientation();
      await _setFlashMode();
      // _aspectRatio = _cameraController.value.aspectRatio;
      _canSwitchCamera = true;
      _loading = false;
      if (mounted) setState(() {});
    } on CameraException catch (e, stack) {
      GA.analytics.logEvent(name: 'camera_init_exception');
      FirebaseCrashlytics.instance.recordError(e, stack);
      if (mounted)
        Utils.showErrorAlert(context, _l10n.error, _l10n.somethingWentWrong, () {
          Navigator.pop(context);
        });
    } catch (e, stack) {
      GA.analytics.logEvent(name: 'camera_init_error');
      FirebaseCrashlytics.instance.recordError(e, stack);
      if (mounted)
        Utils.showErrorAlert(context, _l10n.error, _l10n.somethingWentWrong, () {
          Navigator.pop(context);
        });
    }
  }
  Widget _cameraPreviewAndButtons() {
    return Stack(
      children: [
        if (_cameraController != null && _cameraController.value.isInitialized) _cameraPreview(),
        _overlayAndInfo(),
        Column(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            Padding(
              padding: const EdgeInsets.all(20),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  _switchCameraButton(),
                  _takePhotoButton(),
                  _closePreviewButton(),
                ],
              ),
            ),
          ],
        ),
      ],
    );
  }
  Widget _cameraPreview() {
    final size = MediaQuery.of(context).size;
    var scale = size.aspectRatio * _cameraController.value.aspectRatio;
    if (scale < 1) scale = 1 / scale;
    return Align(
      alignment: Alignment.center,
      child: Transform.scale(
        scale: scale,
        child: CameraPreview(_cameraController),
      ),
    );
  }

@github-actions github-actions bot removed the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Sep 22, 2021
@darshankawar
Copy link
Member

@carman247
If you are referring the official camera plugin example, it makes use of WidgetsBindingObserver which helps to correctly dispose the controller. Can you try that if you aren't using that already ?

@darshankawar darshankawar added the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Sep 22, 2021
@carman247
Copy link
Author

@darshankawar I'm pretty sure I'm implementing that although I'm not really sure what's it doing apart from adding the observer in initState and removing it in dispose.

Can you explain how the WidgetsBindingObserver is helping to dispose the controller? Is there something else I need to do other than what I've mentioned above?

I'm also using didChangeAppLifecycleState to dispose, like this:

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    GA.analytics.logEvent(name: 'camera_did_app_change_lifecycle', parameters: {'appLifecycleState': state.toString()});
    final CameraController cameraController = _cameraController;
    if (cameraController == null || !cameraController.value.isInitialized) {
      return;
    }
    if (state == AppLifecycleState.inactive) {
      cameraController.dispose();
    } else if (state == AppLifecycleState.resumed) {
      _initController();
    }
  }

@github-actions github-actions bot removed the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Sep 22, 2021
@darshankawar
Copy link
Member

@carman247 Thanks. I see what you are saying. I tried the official plugin example on latest master and stable and ran it on iOS. It doesn't replicate immediately and we have to keep switching between front and back camera few times, that leads to below exception:

console error log
======== Exception caught by widgets library =======================================================
The following CameraException was thrown building ValueListenableBuilder<CameraValue>(dirty, state: _ValueListenableBuilderState<CameraValue>#7da39):
CameraException(Disposed CameraController, buildPreview() was called on a disposed CameraController.)

The relevant error-causing widget was: 
  CameraPreview CameraPreview:file:///Users/dhs/Documents/NCFlutter/ios_bug/lib/main.dart:189:16
When the exception was thrown, this was the stack: 
#0      CameraController._throwIfNotInitialized (package:camera/src/camera_controller.dart:821:7)
#1      CameraController.buildPreview (package:camera/src/camera_controller.dart:575:5)
#2      CameraPreview.build.<anonymous closure> (package:camera/src/camera_preview.dart:34:57)
#3      _ValueListenableBuilderState.build (package:flutter/src/widgets/value_listenable_builder.dart:187:26)
#4      StatefulElement.build (package:flutter/src/widgets/framework.dart:4700:27)
#5      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4583:15)
#6      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4758:11)
#7      Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#8      StatefulElement.update (package:flutter/src/widgets/framework.dart:4790:5)
#9      Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#10     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#11     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#12     StatelessElement.update (package:flutter/src/widgets/framework.dart:4664:5)
#13     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#14     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6120:14)
#15     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#16     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6120:14)
#17     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#18     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6120:14)
#19     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#20     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6120:14)
#21     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#22     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6120:14)
#23     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#24     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#25     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#26     StatelessElement.update (package:flutter/src/widgets/framework.dart:4664:5)
#27     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#28     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#29     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#30     ProxyElement.update (package:flutter/src/widgets/framework.dart:4938:5)
#31     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#32     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5618:32)
#33     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6274:17)
#34     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#35     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#36     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#37     StatelessElement.update (package:flutter/src/widgets/framework.dart:4664:5)
#38     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#39     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#40     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#41     ProxyElement.update (package:flutter/src/widgets/framework.dart:4938:5)
#42     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#43     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#44     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#45     ProxyElement.update (package:flutter/src/widgets/framework.dart:4938:5)
#46     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#47     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5618:32)
#48     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6274:17)
#49     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#50     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#51     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4758:11)
#52     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#53     StatefulElement.update (package:flutter/src/widgets/framework.dart:4790:5)
#54     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#55     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#56     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#57     ProxyElement.update (package:flutter/src/widgets/framework.dart:4938:5)
#58     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#59     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#60     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4758:11)
#61     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#62     StatefulElement.update (package:flutter/src/widgets/framework.dart:4790:5)
#63     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#64     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6120:14)
#65     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#66     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#67     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#68     StatelessElement.update (package:flutter/src/widgets/framework.dart:4664:5)
#69     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#70     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6120:14)
#71     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#72     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#73     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4758:11)
#74     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#75     StatefulElement.update (package:flutter/src/widgets/framework.dart:4790:5)
#76     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#77     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#78     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4758:11)
#79     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#80     StatefulElement.update (package:flutter/src/widgets/framework.dart:4790:5)
#81     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#82     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#83     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#84     ProxyElement.update (package:flutter/src/widgets/framework.dart:4938:5)
#85     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#86     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#87     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#88     StatelessElement.update (package:flutter/src/widgets/framework.dart:4664:5)
#89     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#90     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#91     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4758:11)
#92     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#93     StatefulElement.update (package:flutter/src/widgets/framework.dart:4790:5)
#94     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#95     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#96     Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#97     ProxyElement.update (package:flutter/src/widgets/framework.dart:4938:5)
#98     Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#99     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#100    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4758:11)
#101    Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#102    StatefulElement.update (package:flutter/src/widgets/framework.dart:4790:5)
#103    Element.updateChild (package:flutter/src/widgets/framework.dart:3370:15)
#104    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4608:16)
#105    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4758:11)
#106    Element.rebuild (package:flutter/src/widgets/framework.dart:4311:5)
#107    BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2578:33)
#108    WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#109    RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:319:5)
#110    SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1145:15)
#111    SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1082:9)
#112    SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:996:5)
#116    _invoke (dart:ui/hooks.dart:166:10)
#117    PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:270:5)
#118    _drawFrame (dart:ui/hooks.dart:129:31)
(elided 3 frames from dart:async)
====================================================================================================






stable, master flutter doctor -v
[✓] Flutter (Channel stable, 2.5.1, on Mac OS X 10.15.4 19E2269 darwin-x64,
    locale en-GB)
    • Flutter version 2.5.1 at /Users/dhs/documents/fluttersdk/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision ffb2ecea52 (2 days ago), 2021-09-17 15:26:33 -0400
    • Engine revision b3af521a05
    • Dart version 2.14.2

[✓] Android toolchain - develop for Android devices (Android SDK version 30)
    • Android SDK at /Users/dhs/Library/Android/sdk
    • Platform android-30, build-tools 30.0.3
    • ANDROID_HOME = /Users/dhs/Library/Android/sdk
    • Java binary at: /Users/dhs/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/202.7486908/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
    • All Android licenses accepted.    

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.3, Build version 12C33
    • CocoaPods version 1.10.1

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

[✓] Android Studio (version 4.1)
    • Android Studio at /Users/dhs/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/202.7486908/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
      1.8.0_242-release-1644-b3-6915495)        

[✓] VS Code (version 1.57.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.21.0

[✓] Connected device (4 available)
    • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
      ios            • iOS 14.4.1 18D61
    • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
      ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
    • macOS (desktop)            • macos                                    •
      darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
    • Chrome (web)               • chrome                                   •
      web-javascript • Google Chrome 93.0.4577.82


• No issues found!

[✓] Flutter (Channel master, 2.6.0-6.0.pre.170, on Mac OS X 10.15.4 19E2269
    darwin-x64, locale en-GB)
    • Flutter version 2.6.0-6.0.pre.170 at
      /Users/dhs/documents/fluttersdk/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 9ae8d23dc4 (35 minutes ago), 2021-09-21 21:21:43 -0700
    • Engine revision b0f3c0f7e4
    • Dart version 2.15.0 (build 2.15.0-127.0.dev)

[✓] Android toolchain - develop for Android devices (Android SDK version 30)
    • Android SDK at /Users/dhs/Library/Android/sdk
    • Platform android-30, build-tools 30.0.3
    • ANDROID_HOME = /Users/dhs/Library/Android/sdk
    • Java binary at: /Users/dhs/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/202.7486908/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
    • All Android licenses accepted.    

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.5.1, Build version 12E507
    • CocoaPods version 1.10.1

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

[✓] Android Studio (version 4.1)
    • Android Studio at /Users/dhs/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/202.7486908/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
      1.8.0_242-release-1644-b3-6915495)        

[✓] VS Code (version 1.57.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.21.0

[[✓] Connected device (4 available)
    • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
      ios            • iOS 14.4.1 18D61
    • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
      ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
    • macOS (desktop)            • macos                                    •
      darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
    • Chrome (web)               • chrome                                   •
      web-javascript • Google Chrome 93.0.4577.82


• No issues found!




@darshankawar darshankawar added found in release: 2.5 Found to occur in 2.5 found in release: 2.6 Found to occur in 2.6 has reproducible steps The issue has been confirmed reproducible and is ready to work on p: camera The camera plugin p: first party platform-ios iOS applications specifically and removed in triage Presently being triaged by the triage team labels Sep 22, 2021
@darshankawar darshankawar changed the title [camera] should calling dispose on a controller set isInitialized to false? [camera][ios] : Switching between front and back camera throws CameraException(Disposed CameraController, buildPreview() was called on a disposed CameraController.) exception. Sep 22, 2021
@carman247
Copy link
Author

@darshankawar no problem!

@stuartmorgan stuartmorgan added the P2 Important issues not at the top of the work list label Sep 23, 2021
@carman247
Copy link
Author

carman247 commented Oct 1, 2021

Hi, I'm not sure if this is helpful at all but I think I've managed to trigger this as well and have potentially found a fix for my situation.

Previously my widgets we checking _cameraController != null && _cameraController.value.isInitialized like this

  Widget _cameraPreview() {
    final size = MediaQuery.of(context).size;
    var scale = size.aspectRatio * _cameraController.value.aspectRatio;
    if (scale < 1) scale = 1 / scale;
    return Align(
      alignment: Alignment.center,
      child: Transform.scale(
        scale: scale,
        child: CameraPreview(_cameraController),
      ),
    );
  }

  Widget _cameraPreviewAndButtons() {
    return Stack(
      children: [
        if (_cameraController != null && _cameraController.value.isInitialized) _cameraPreview(),
        _overlayAndInfo(),
        Column(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            Padding(
              padding: const EdgeInsets.all(20),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  _switchCameraButton(),
                  _takePhotoButton(),
                  _closePreviewButton(),
                ],
              ),
            ),
          ],
        ),
      ],
    );
  }

I noticed that didChangeAppLifecycleState was triggering AppLifecycleState.inactive when a camera permission request was displayed, before the controller started initialising. However, it only triggered AppLifecycleState.resumed right after the controller had finished initialising.

  void didChangeAppLifecycleState(AppLifecycleState state) {
    GA.analytics.logEvent(name: 'camera_did_app_change_lifecycle', parameters: {'appLifecycleState': state.toString()});
    final CameraController cameraController = _cameraController;
    if (cameraController == null || !cameraController.value.isInitialized) {
      return;
    }
    if (state == AppLifecycleState.inactive) {
      cameraController.dispose();
    } else if (state == AppLifecycleState.resumed) {
      _initController();
    }
  }

I am also calling setState after the controller has initialised, which caused the widgets to rebuild, and throw the error.

Moving the null and isInitialized checks into the other widget, and right before the CameraPreview has stopped this from happening.

  Widget _cameraPreview() {
    final size = MediaQuery.of(context).size;
    var scale = size.aspectRatio * _cameraController.value.aspectRatio;
    if (scale < 1) scale = 1 / scale;
    return Align(
      alignment: Alignment.center,
      child: Transform.scale(
        scale: scale,
        // child: CameraPreview(_cameraController) : Container(),
        child: _cameraController != null && _cameraController.value.isInitialized ? CameraPreview(_cameraController) : Container(),
      ),
    );
  }

  Widget _cameraPreviewAndButtons() {
    return Stack(
      children: [
        // if (_cameraController != null && _cameraController.value.isInitialized) _cameraPreview(),
        _cameraPreview(),
        _overlayAndInfo(),
        Column(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            Padding(
              padding: const EdgeInsets.all(20),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  _switchCameraButton(),
                  _takePhotoButton(),
                  _closePreviewButton(),
                ],
              ),
            ),
          ],
        ),
      ],
    );
  }

@jtkeyva
Copy link

jtkeyva commented Feb 7, 2022

Thanks. I thought it seemed to happen less in release mode, but not sure

@carman247
Copy link
Author

carman247 commented Feb 14, 2022

This is definitely an issue for me still ... I'm not quite sure what the problem is, it only seems to happen on iOS and only occurs when the app enters AppLifecycleState.inactive (double tapping the home button) before the controller has had a chance to initialise, then quickly entering AppLifecycleState.resumed again.

Not only can I reproduce in my app but also in the example app provided.

Also, is anyone able to explain the significance/importance of creating a copy of the current CameraController e.g.

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    final CameraController? cameraController = controller;

    // App state changed before we got the chance to initialize.
    if (cameraController == null || !cameraController.value.isInitialized) {
      return;
    }

    if (state == AppLifecycleState.inactive) {
      cameraController.dispose();
    } else if (state == AppLifecycleState.resumed) {
      onNewCameraSelected(cameraController.description);
    }
  }

I find this causes confusion and don't really understand why the original CameraController can't be used.

@snadon
Copy link

snadon commented Feb 21, 2022

Just adding to this issue. I'm getting this exception in one of my app. The first screen allow the user to scan with the qr_mobile_vision package and the following screen allow the user to take pictures with the camera plugin.

This exception is thrown once in a while in Android and iOS devices.

Flutter is latest stable and my current package versions are:

camera: 0.9.4+11
qr_mobile_vision: 3.0.1

@danPyk
Copy link

danPyk commented Aug 4, 2022

I have the same problem on phisical Motorola g8 with Android 11. My code is pretty much copy-paste from example: https://github.com/danPyk/flutter_tensor_flow/blob/master/lib/obj_det/front/camera.dart
I've tested code also on emulator (Pixel 5, Android 13) and everything worked fine.

Error occurs when Im putting app to sleep (state: AppLifecycleState.paused) by using phisical button and also when I'm moving back to app. Error log: https://pastebin.com/Yqw2QYgy

@lynrin
Copy link

lynrin commented Nov 5, 2022

It seems that the CameraPreview needs to be completely released from the Widget tree before disposing the CameraController.

Use WidgetsBinding.instance.addPostFrameCallback to wait for the frame to update after the setState call before calling dispose() on the CameraController.

final cameraController = _cameraController;

_cameraController = null;
if (mounted) {
  setState(() {});

  // Wait for the post frame callback.
  final completerPostFrameCallback = Completer<Duration>();
  WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
    completerPostFrameCallback.complete(timeStamp);
  });
  await completerPostFrameCallback.future;
}

return cameraController!.dispose();

Whole Dart code.

import 'dart:async';

import 'package:camera/camera.dart';
import 'package:flutter/cupertino.dart';

void main() {
  runApp(CameraTest());
}

class CameraTest extends StatefulWidget {

  @override
  State createState() => CameraTestState();
}

class CameraTestState extends State<CameraTest> with WidgetsBindingObserver {
  CameraController? _cameraController;

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

    WidgetsBinding.instance.addObserver(this);

    _initCameraController();
  }

  @override
  void dispose() {
    _disposeCameraController();

    WidgetsBinding.instance.removeObserver(this);

    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (AppLifecycleState.paused == state) {
      _disposeCameraController();
    } else {
      if (_cameraController == null) {
        _initCameraController();
      }
    }
  }

  @override
  Widget build(BuildContext context) {

    return Directionality(
      textDirection: TextDirection.ltr,
      child: Stack(
        alignment: Alignment.center,
        children: [
          if (_cameraController?.value.isInitialized == true)
            CameraPreview(_cameraController!),
        ],
      ),
    );
  }

  Future<void> _initCameraController() async {
    if (_cameraController != null) {
      return Future.value();
    }

    final List<CameraDescription> cameras = await availableCameras();
    final descriptionIndex = cameras.indexWhere(
            (element) => element.lensDirection == CameraLensDirection.back
    );
    final CameraDescription description = cameras.elementAt(descriptionIndex);

    _cameraController = CameraController(
      description,
      ResolutionPreset.medium,
      enableAudio: false,
    );

    await _cameraController!.initialize();

    if (mounted) {
      setState(() {});
    }
  }

  Future<void> _disposeCameraController() async {
    if (_cameraController == null) {
      return Future.value();
    }

    final cameraController = _cameraController;

    _cameraController = null;
    if (mounted) {
      setState(() {});

      // Wait for the post frame callback.
      final completerPostFrameCallback = Completer<Duration>();
      WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
        completerPostFrameCallback.complete(timeStamp);
      });
      await completerPostFrameCallback.future;
    }

    return cameraController!.dispose();
  }
}

@flutter-triage-bot flutter-triage-bot bot added the package flutter/packages repository. See also p: labels. label Jul 5, 2023
@Hixie Hixie removed the plugin label Jul 6, 2023
@flutter-triage-bot flutter-triage-bot bot added multiteam-retriage-candidate team-ios Owned by iOS platform team triaged-ios Triaged by iOS platform team labels Jul 8, 2023
@mikeabbott10
Copy link

mikeabbott10 commented Aug 30, 2023

@lynrin the setState call from _disposeCameraController causes a _lifecycleState != _ElementLifecycle.defunct assertion fail:

'package:flutter/src/widgets/framework.dart': Failed assertion: line 4622 pos 12: '_lifecycleState != _ElementLifecycle.defunct': is not true.
I/flutter (30643): #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
I/flutter (30643): #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
I/flutter (30643): #2      Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4622:12)
I/flutter (30643): #3      State.setState (package:flutter/src/widgets/framework.dart:1159:15)
I/flutter (30643): #4      CameraWidget._disposeCameraController (package:we_clubs/widgets/camera_widget.dart:93:7)
I/flutter (30643): #5      CameraWidget.dispose (package:we_clubs/widgets/camera_widget.dart:23:5)
I/flutter (30643): #6      _TakeBackPictureState.dispose (package:we_clubs/screens/content/socialroom/new_post/take_back_picture.dart:65:11)
I/flutter (30643): #7      StatefulElement.unmount (package:flutter/src/widgets/framework.dart:5297:11)
I/flutter (30643): #8      _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1953:13)
I/flutter (30643): #9      _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1951:7)
I/flutter (30643): #10     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:6421:14)
I/flutter (30643): #11     _InactiveElement

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
found in release: 2.5 Found to occur in 2.5 found in release: 2.6 Found to occur in 2.6 has reproducible steps The issue has been confirmed reproducible and is ready to work on p: camera The camera plugin P2 Important issues not at the top of the work list package flutter/packages repository. See also p: labels. platform-ios iOS applications specifically team-ios Owned by iOS platform team triaged-ios Triaged by iOS platform team
Projects
None yet
Development

No branches or pull requests

9 participants