Skip to content

[camera] Fix CameraController may update after dispose and crash with "used after being disposed. (fixes #184959)#11498

Draft
Istiak-Ahmed78 wants to merge 3 commits intoflutter:mainfrom
Istiak-Ahmed78:fix
Draft

[camera] Fix CameraController may update after dispose and crash with "used after being disposed. (fixes #184959)#11498
Istiak-Ahmed78 wants to merge 3 commits intoflutter:mainfrom
Istiak-Ahmed78:fix

Conversation

@Istiak-Ahmed78
Copy link
Copy Markdown

Fixes flutter/flutter#184959

Description

Fixed a critical race condition bug in the camera package where async callbacks in CameraController._initializeWithDescription() could execute after dispose() is called, causing a crash: "A CameraController was used after being disposed. Once you have called dispose() on a CameraController, it can no longer be used."

Root Cause

In _initializeWithDescription(), three async fire-and-forget callbacks had no dispose guards:

// Line 341-346: _deviceOrientationSubscription listener
_deviceOrientationSubscription ??= CameraPlatform.instance
    .onDeviceOrientationChanged()
    .listen((DeviceOrientationChangedEvent event) {
      value = value.copyWith(deviceOrientation: event.orientation);  // NO GUARD!
    });

// Line 363-365: onCameraError callback
unawaited(
  CameraPlatform.instance.onCameraError(_cameraId).first.then((
    CameraErrorEvent event,
  ) {
    value = value.copyWith(errorDescription: event.description);  // NO GUARD!
  }),
);

// Line 358-371: Main value assignment (THE CRASH POINT)
value = value.copyWith(
  isInitialized: true,
  description: description,
  previewSize: await initializeCompleter.future.then(...),
  exposureMode: await initializeCompleter.future.then(...),
  ...
);  // NO GUARD! This is line 364 in the crash stack trace

The Issue: When dispose() is called while these callbacks are still pending:

  1. dispose() sets _isDisposed = true
  2. Async callbacks later resolve
  3. They call value = ... which calls notifyListeners()
  4. notifyListeners() throws because the controller is disposed

Solution

Added if (!_isDisposed) guards before every value = ... assignment in async contexts within _initializeWithDescription().

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I added/updated tests for this change.
  • I used self-descriptive commit messages.

@Istiak-Ahmed78 Istiak-Ahmed78 changed the title CameraController may update after dispose and crash with "used after being disposed. (fixes #184959) Fix CameraController may update after dispose and crash with "used after being disposed. (fixes #184959) Apr 13, 2026
@Istiak-Ahmed78 Istiak-Ahmed78 changed the title Fix CameraController may update after dispose and crash with "used after being disposed. (fixes #184959) [camera] Fix CameraController may update after dispose and crash with "used after being disposed. (fixes #184959) Apr 13, 2026
.onDeviceOrientationChanged()
.listen((DeviceOrientationChangedEvent event) {
value = value.copyWith(deviceOrientation: event.orientation);
if (!_isDisposed) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could these use early returns? That would reduce the indent changes in code.

Just a personal preference though.

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.

Thanks for the suggestion! Early returns would definitely improve the code.

However, this PR is still in draft. @mbcorona from the Flutter team recently
discovered a deeper issue: ImageReader fails to drain pending frames on dispose,
flooding the queue with 290+ orphaned callbacks
.
flutter/flutter#184959 (comment)

My current fix addresses the symptom (guarding state updates). but the root cause is the improper ImageReader cleanup at the native
level. So I'm currently reinvestigating the issue

Once I resolve the frame leakage issue, I'll:

  • Apply your early return suggestion
  • Update the fix comprehensively
  • Mark it ready for review

Thanks again for your suggestion!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Before marking it ready for review, please update the PR to use and follow our actual checklist, rather than an AI-hallucinated checklist.

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.

[camera] CameraController may update after dispose and crash with "used after being disposed"

3 participants