Skip to content

[camera] Guard CameraController value updates after dispose#11861

Open
motucraft wants to merge 1 commit into
flutter:mainfrom
motucraft:fix/camera-controller-update-after-dispose
Open

[camera] Guard CameraController value updates after dispose#11861
motucraft wants to merge 1 commit into
flutter:mainfrom
motucraft:fix/camera-controller-update-after-dispose

Conversation

@motucraft

@motucraft motucraft commented Jun 8, 2026

Copy link
Copy Markdown

Guards the value assignments in _initializeWithDescription so they no longer run after dispose(), fixing the "A CameraController was used after being disposed." crash.

These user-level steps, using the reproduction app in flutter/flutter#184959, trigger the crash:

  1. Open camera A.
  2. Close it before initialization finished (disposing the controller).
  3. Open camera B.
  4. Adjust an exposure/zoom slider.

When the controller is disposed while initialization is still in flight, the deferred value updates could call notifyListeners() on a disposed ChangeNotifier, throwing the exception above.
This adds if (!_isDisposed) guards (and awaits the initialization event only once) so those updates are skipped after dispose.
The guard lives in shared Dart code, so it also covers iOS and web.

Part of flutter/flutter#184959.

This PR addresses only the Dart-layer crash. As @mbcorona noted in flutter/flutter#184959 (comment), the ANR observed on Android has a separate native root cause (ImageReader not severing the stream on dispose, "BufferQueue has been abandoned"), which was routed to team-android. That native issue is not addressed here and should be handled separately in camera_android_camerax.

Pre-Review Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. 2

@google-cla

google-cla Bot commented Jun 8, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@motucraft motucraft force-pushed the fix/camera-controller-update-after-dispose branch from bc5e55d to 021bc1c Compare June 8, 2026 06:30
Guards the `value` assignments in `_initializeWithDescription` so they no longer run after `dispose()`, fixing the "A CameraController was used after being disposed." crash.

Addresses the Dart-side crash in flutter/flutter#184959.
The native Android ANR discussed in that issue is a separate root cause and is not covered here.
@motucraft motucraft force-pushed the fix/camera-controller-update-after-dispose branch from 021bc1c to 498851b Compare June 9, 2026 02:03
@motucraft motucraft marked this pull request as ready for review June 9, 2026 02:47

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request prevents CameraController from updating its value after being disposed by adding _isDisposed checks in the device orientation listener, the camera error listener, and after awaiting initialization. It also includes corresponding unit tests and bumps the package version to 0.12.1. Feedback suggests cancelling and nullifying _deviceOrientationSubscription in the PlatformException catch block of _initializeWithDescription to prevent resource leaks and ensure successful retries.

Comment on lines 385 to 386
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If _initializeWithDescription fails with a PlatformException, the _deviceOrientationSubscription that was created at the start of the method is left active and is never cancelled. This leads to a resource leak, and also prevents the subscription from being recreated if initialize() is retried (since _deviceOrientationSubscription remains non-null).

We should cancel the subscription and set it to null in the catch block to ensure proper cleanup and allow successful retries.

Suggested change
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
} on PlatformException catch (e) {
unawaited(_deviceOrientationSubscription?.cancel());
_deviceOrientationSubscription = null;
throw CameraException(e.code, e.message);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/gemini

This is pre-existing behavior unrelated to the bug this PR fixes (a CameraController updating its value after being disposed).

The subscription is already cancelled in dispose(). It only lingers if the controller is never disposed, which is independent of whether initialization succeeds or fails. And since onDeviceOrientationChanged() is a global stream, reusing it on retry is intentional rather than a leak.

To keep this PR focused, I'd prefer to leave this out of scope, but I'm happy to address it in a separate PR if a maintainer thinks it's worth changing.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant