Sitelet https://github.com/flutter/packages/pull/12582
Skip to content

[camera_android_camerax] Fix exposure offset setting error thrown when canceled by a new request - #12582

Merged
auto-submit[bot] merged 7 commits into
flutter:mainfrom
camsim99:pos_harness_trial
Sep 30, 2026
Merged

auto-submit[bot] merged 7 commits into
flutter:mainfrom
camsim99:pos_harness_trial

Conversation

@camsim99

@camsim99 camsim99 commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

When setting exposure offset is canceled (caused by a user submitting too many requests or the camera closing) return the rounded exposure offset instead of throwing an error, causing the app to crash.

Fixes flutter/flutter#148521.


Project One Shot Demonstration. Implementation plan used to execute this one shot: 781681d

Related (attempt without harness): #12580

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

@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Aug 24, 2026

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

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.

Code Review

This pull request adds an implementation plan (implementation_plan.md) for the camera_android_camerax package. The document outlines proposed changes to resolve a camera preview freeze issue by handling exposure offset and focus/metering cancellations gracefully, alongside updating the corresponding tests. As there are no review comments, no further feedback is provided.


## User Review Required
> [!NOTE]
> The platform interface for `setExposureOffset` specifies that it should return the rounded offset value that was set. We will be fixing a bug where it previously returned the raw integer index from CameraX. This is technically a bug fix but alters the return value to what the platform interface actually specifies.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I would like you to provide more proof here. The CameraX docs mention that it returns "new target exposure value" which we then return. How is this different from what the platform interface setExposureOffset specifies? Please provide a code pointer to that, as well.

1. Throws a `CameraException` which crashes the local operation.
2. Adds an error string to the global `cameraErrorStreamController`.

Adding an error to `cameraErrorStreamController` notifies the frontend `CameraController` that the camera has encountered a critical, unrecoverable failure. This is what causes the app's camera preview to freeze and stop responding, as the app tears down the camera session in response to the global error.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is there a good way for us to notify the developer that their request was canceled without throwing an exception an undoing what this fix intends?

>
> If we *don't* throw an exception, we can just return the rounded offset that they requested. Since a new request has already been submitted to replace this one, the slider will eventually settle on the final request's value.
>
> We could technically throw a `CameraException` with a specific code like `exposureOffsetCanceled`, but returning the requested value gracefully (which is what we currently propose) is usually the standard way to handle superseded rapid-fire requests without littering the developer's console/UI with errors. What do you prefer?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm ok with moving forward without an exception since the issue appears to be the intermediate values set between the old value and the desired value. The only other relevant situation for the exception appears to be when the camera is closed (docs), in which case failing to set the exposure offset is not an error. Please update the plan with this decision and reasoning.

1. Validating the code formatting with `dart run $REPO_ROOT/script/tool/bin/flutter_plugin_tools.dart format --packages camera_android_camerax`.
2. Validating static analysis with `dart run $REPO_ROOT/script/tool/bin/flutter_plugin_tools.dart analyze --packages camera_android_camerax`.
3. Validating tests with `dart run $REPO_ROOT/script/tool/bin/flutter_plugin_tools.dart dart-test --packages camera_android_camerax`.
4. Bumping the patch version and generating a `CHANGELOG.md` entry via `dart run $REPO_ROOT/script/tool/bin/flutter_plugin_tools.dart update-release-info --version=minimal --base-branch=origin/main --changelog="Fix exposure offset slider freezing camera preview."`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The changelog should also mention the setExposureOffset return value fix.

@camsim99
camsim99 requested a review from reidbaker September 2, 2026 18:04

@reidbaker reidbaker left a comment

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.

will need to delete plan

).thenAnswer((_) async => Future<int?>.value());

expect(() => camera.setExposureOffset(cameraId, offset), throwsA(isA<CameraException>()));
expect(await camera.setExposureOffset(cameraId, offset), equals(5.0));

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.

where does magic number 5 come from? also if the value could not be set then should this test instead take any number?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

5 is the exposure offset value being set in the test. 5 is expected but the return value of setExposureOffset specifically is the rounded number of the value being set, so I updated the test to do some rounding to make that clearer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I fixed the "magic number" issue manually here but filed #13081 to hopefully prevent agents from doing this in the future. At the very least in this case, the agent could have used the offset variable set earlier in the test.

@camsim99 camsim99 added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 30, 2026
@auto-submit
auto-submit Bot merged commit 8d0f931 into flutter:main Sep 30, 2026
13 checks passed
jesswrd pushed a commit to jesswrd/flutter that referenced this pull request Oct 1, 2026
…er#193641)

flutter/packages@0ba9a82...d5ec6db

2026-10-01 faheemabbas766@gmail.com [tool] Enforce README package table
order (flutter/packages#12316)
2026-09-30 jessiewong401@gmail.com [various] Allow plugin example apps
to build and test on JDK 25 (flutter/packages#13031)
2026-09-30 149176071+m1roxx@users.noreply.github.com [go_router] Expose
Navigator clipBehavior on ShellRoute and StatefulShellBranch
(flutter/packages#12646)
2026-09-30 stuartmorgan@google.com [google_maps_flutter] Convert unit
tests to Kotlin (flutter/packages#13072)
2026-09-30 36861262+QuncCccccc@users.noreply.github.com [material_ui]
Migrate M3 ListTile template to use new gen_defaults
(flutter/packages#13056)
2026-09-30 15619084+vashworth@users.noreply.github.com Allow tests to
use macOS 15.7 or macOS 26.6 (flutter/packages#13007)
2026-09-30 43054281+camsim99@users.noreply.github.com
[camera_android_camerax] Fix exposure offset setting error thrown when
canceled by a new request (flutter/packages#12582)
2026-09-30 engine-flutter-autoroll@skia.org Roll Flutter from
55b8f88 to d649d2b (27 revisions) (flutter/packages#13080)
2026-09-30 36861262+QuncCccccc@users.noreply.github.com [material_ui]
Migrate M3 InputDecorator template to use new gen_defaults
(flutter/packages#13024)
2026-09-30 tarrinneal@gmail.com [pigeon] Fix JNI/FFI typed data memory
lifetime bugs and update docs (flutter/packages#13061)
2026-09-30 43054281+camsim99@users.noreply.github.com
[camera_android_camerax] Correct `pre-push` skill version validation
logic (flutter/packages#12371)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/flutter-packages-flutter-autoroll
Please CC flutter-ecosystem@google.com on the revert to ensure that a
human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD p: camera

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[camera_android_camerax] Setting exposure compensation index was canceled due to the camera being closed or a new request being submitted

2 participants