Skip to content

[camera] Add setImageQuality for JPEG compression control - #11155

Open
Bolling88 wants to merge 2 commits into
flutter:mainfrom
Bolling88:fix-set-quality
Open

[camera] Add setImageQuality for JPEG compression control#11155
Bolling88 wants to merge 2 commits into
flutter:mainfrom
Bolling88:fix-set-quality

Conversation

@Bolling88

@Bolling88 Bolling88 commented Mar 2, 2026

Copy link
Copy Markdown
Contributor

Adds setJpegImageQuality(int quality) (1–100) to the app-facing camera package, forwarding to the platform interface method.

This PR is now scoped down to camera only. The platform interface and implementations already landed and published separately:

Package Version Landed in
camera_platform_interface 2.13.0 #11155 (split out)
camera_platform_interface 2.13.1 #12123 — default implementation made a no-op
camera_android_camerax 0.7.4 #11616
camera_avfoundation 0.10.2 #11616

This PR

Package Version
camera 0.12.0+2 → 0.12.1
  • Adds CameraController.setJpegImageQuality, with 1–100 range validation and CameraException translation.
  • Bumps constraints to the published versions that implement it: camera_platform_interface: ^2.13.1, camera_android_camerax: ^0.7.4, camera_avfoundation: ^0.10.2. No dependency_overrides.
  • Per the review discussion, the platform interface default is a no-op rather than an UnimplementedError, so calling this is always safe. The dartdoc says the setting is best-effort and links to [camera] Support setJpegImageQuality on web and Windows flutter#191790 for the current state of platform support (web and Windows are not implemented yet).

Fixes flutter/flutter#183229

Pre-Review Checklist

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 Mar 2, 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.

@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 introduces the setImageQuality method to control JPEG compression quality for still image captures across the camera plugin. The implementation is federated, with platform-specific logic for Android (Camera2 and CameraX) and iOS. The Android Camera2 implementation uses CaptureRequest.JPEG_QUALITY. The CameraX implementation recreates the ImageCapture use case to apply the quality setting, preserving any locked capture orientation. The iOS implementation re-encodes JPEG images to the specified quality while retaining EXIF metadata. The changes are accompanied by updates to the platform interface, method channels, and comprehensive tests for each platform.

@stuartmorgan-g

Copy link
Copy Markdown
Collaborator

Thanks for the contribution!

Pre-Review Checklist

In the future, please use our checklist rather than replacing it with one you have invented. Our checklist is standardized for a reason.

Related issues

Fixes flutter/flutter#60397

Please provide the actual issue this is related to. That's not even a camera issue.

@Bolling88

Copy link
Copy Markdown
Contributor Author

Thanks for the contribution!

Pre-Review Checklist

In the future, please use our checklist rather than replacing it with one you have invented. Our checklist is standardized for a reason.

Related issues

Fixes flutter/flutter#60397

Please provide the actual issue this is related to. That's not even a camera issue.

Sorry for that! My first contribution, and still learning!

Regarding the issue, I just took a placeholder and forgot to change it. This is rather a feature improvement than resolving an issue, but I found and linked a closed issue where one of the comments requested the possibility to set image quality. If that is not enough, can I just create a new issue?

@stuartmorgan-g

Copy link
Copy Markdown
Collaborator

This is rather a feature improvement than resolving an issue

As with almost every project, we track bug reports and feature requests in the same issue tracker.

but I found and linked a closed issue where one of the comments requested the possibility to set image quality. If that is not enough, can I just create a new issue?

An issue that was fixed seven years ago does not provide an explanation of what current use case needs new APIs. If there isn't an issue covering the use case you are trying to address, then you should file one.

@Bolling88

Copy link
Copy Markdown
Contributor Author

This is rather a feature improvement than resolving an issue

As with almost every project, we track bug reports and feature requests in the same issue tracker.

but I found and linked a closed issue where one of the comments requested the possibility to set image quality. If that is not enough, can I just create a new issue?

An issue that was fixed seven years ago does not provide an explanation of what current use case needs new APIs. If there isn't an issue covering the use case you are trying to address, then you should file one.

Thank you for your patience. I have created a new issue ticket that clearly explains the problem/feature.

Comment thread packages/camera/camera/CHANGELOG.md Outdated
@@ -1,3 +1,7 @@
## 0.12.1

* Adds `setImageQuality` for controlling JPEG compression quality.

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.

is this function JPEG only? if so, better to put it in the function name

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 API is intentionally named setImageQuality rather than setJpegQuality for a couple of reasons:

  1. It's consistent with Flutter's existing image_picker plugin which uses imageQuality with the same 1–100 int scale.
  2. It's more future-proof — if quality control is ever extended to other formats (e.g. HEIF), the API name still makes sense.
  3. From the app developer's perspective, they're setting the quality of the captured image, regardless of the underlying format.

The current implementation applies to JPEG only (HEIF passes through unmodified), but the abstraction level feels right for a cross-platform API.

let savePhotoDelegate = SavePhotoDelegate(
path: path,
ioQueue: photoIOQueue,
imageQuality: fileExtension == "jpg" && imageQuality < 100 ? imageQuality : nil,

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.

what if file extension is jpeg?
does nil quality mean 100% quality?
can quality be a floating point 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.

Good questions!

  • File extension: The extension is always set by us in captureToFile (either "heif" or "jpg"), so "jpeg" won't occur. But I've changed the check to fileExtension != "heif" which is more robust and format-oriented.
  • nil quality: nil means no quality override was requested — the original photo data is written as-is without re-encoding. This is the default path for users who never call setImageQuality.
  • Float: The int 1–100 scale is consistent with Flutter's image_picker plugin (imageQuality parameter) and with Android's native CaptureRequest.JPEG_QUALITY (byte 0–100) and CameraX's ImageCapture.Builder.setJpegQuality(int). We convert to float internally on iOS with CGFloat(quality) / 100.0. A cross-platform API should use a single consistent type, and int 1–100 is the natural fit for the Android side.

guard let quality = self?.imageQuality, quality < 100 else {
return photo.fileDataRepresentation()
}
guard let originalData = photo.fileDataRepresentation() else {

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.

it seems fileDataRepresentation is used in both case. can reduce duplicate code by putting it in the guard statement above

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.

Good catch! Refactored to call fileDataRepresentation() once at the top, then conditionally re-encode. Done in the latest commit.

guard let originalData = photo.fileDataRepresentation() else {
return nil
}
return Self.reencodeJPEG(data: originalData, quality: quality)

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.

is it guaranteed to be JPEG format at this point? it is not clear from reading this chunk of code, without looking at the caller site.

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.

Yes — the caller (DefaultCamera.captureToFile) only passes a non-nil imageQuality when the format is JPEG (checked via fileExtension != "heif"). I've added an inline comment in the latest commit to make this guarantee visible without needing to check the caller.

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.

This seems to be quite brittle and could very well surprise us down the road. The imageQuality naming suggest that it could be extended to other formats such as HEIF, as you said previously, but in code we are making the assumption that it must be JPEG.

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.

Good point about brittleness. I've added a runtime JPEG format guard inside reencodeJPEG itself — it now checks CGImageSourceGetType and returns the original data unchanged if the source isn't JPEG. This way the method is self-contained and safe even if it's called with non-JPEG data in the future.

Also addressed the other two comments in the same commit:

  • Added inline comments for all CGImageDestinationCreateWithData parameters (the nil is "no additional options")
  • Added a comment on the mutableData as Data return explaining it contains the finalized JPEG after CGImageDestinationFinalize

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.

I'd recommend to move the branching logic to the caller, so that the helper method reencodeJPEG is more focused on just JPEG.

switch type {
  case jpeg: 
    helper that compresses JPEG
   case _:
    return original image
}

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.

Makes sense! Removed the format check from reencodeJPEG — it now focuses purely on JPEG re-encoding. The format branching is already handled by the caller (DefaultCamera.captureToFile only passes imageQuality for JPEG captures via the fileFormat == .jpeg check).

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.

What you did here is in the exact same state discussed in my previous concern (https://github.com/flutter/packages/pull/11155/changes#r2962038676). I was talking about the immediate caller of reencodeJPEG.

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.

Sorry about that! I misunderstood — you meant the immediate caller of reencodeJPEG, not the higher-level caller.

Fixed: photoOutput now explicitly checks the data format using CGImageSourceGetType before calling reencodeJPEG. Non-JPEG data returns unchanged without entering the re-encoding path at all.

guard
let destination = CGImageDestinationCreateWithData(
mutableData as CFMutableData,
"public.jpeg" as CFString,

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.

why hardcode filename? and also wanna double check jpg vs jpeg since you used both in this PR

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 `"public.jpeg"` string was the UTI for the output format passed to `CGImageDestinationCreateWithData`. I've replaced it with `UTType.jpeg.identifier` in the latest commit which is cleaner.

Regarding `jpg` vs `jpeg` — the only place we use `"jpg"` is the file extension (set by us in `captureToFile`), and `"public.jpeg"` / `UTType.jpeg` is the standard UTI for the JPEG format. These are different things (file extension vs format identifier) so the difference is expected.

mutableData as CFMutableData,
"public.jpeg" as CFString,
1,
nil)

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.

can you add comment on what these params are

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.

Added doc comments to the reencodeJPEG method with - Parameters: section explaining both data and quality. Done in the latest commit.

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.

Sorry i meant params of CGImageDestinationCreateWithData (e.g. what does nil mean?)

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.

Added inline comments for each parameter. The nil is documented as "no additional options" — Apple's API accepts an optional dictionary for type-specific options, but none are needed for basic JPEG encoding.

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.

Ah sorry, I misunderstood the first time! Added inline comments for all four CGImageDestinationCreateWithData parameters in the latest commit:

  • mutableData as CFMutableData — output buffer for the encoded image
  • "public.jpeg" as CFString — UTI for JPEG format
  • 1 — imageCount: single image
  • nil — no additional type-specific options (Apple's API accepts an optional dictionary here, but none are needed for basic JPEG encoding)

}

var properties = metadata
properties[kCGImageDestinationLossyCompressionQuality] = CGFloat(quality) / 100.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.

it looks like quality can be a random floating point, and it doesn't have to be a multiple of 0.01? If so, a better API would be just to pass in a float, just like this Apple API.

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 int 1–100 scale was chosen to be consistent across all platforms:

  • Android Camera2: CaptureRequest.JPEG_QUALITY uses a byte (0–100)
  • Android CameraX: ImageCapture.Builder.setJpegQuality(int) uses int 1–100
  • Flutter's own image_picker plugin: imageQuality uses int 0–100

Using a float would match the iOS-native API but would be inconsistent with the Android side and existing Flutter conventions. The conversion CGFloat(quality) / 100.0 is straightforward and lossless for all int values in the 1–100 range.

return data
}

return mutableData as Data

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.

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.

UIImage.jpegData(compressionQuality:) strips all EXIF metadata (GPS coordinates, camera info, orientation, etc.). For a camera plugin, preserving this metadata is important — users expect captured photos to retain location data, orientation, and other properties.

CGImageDestination lets us re-encode the pixels at a lower quality while copying all original metadata properties through CGImageSourceCopyPropertiesAtIndex. I've added a doc comment to reencodeJPEG explaining this rationale.

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.

This is worth documenting, in case of future cleanup that "simplifies" it into a bad state. Could you add a comment?

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.

Good call. Added a comment explaining that mutableData contains the finalized JPEG after CGImageDestinationFinalize and the cast to Data is intentional.

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.

Done! Added a comment above the return explaining that mutableData contains the finalized JPEG after CGImageDestinationFinalize, so future readers won't be tempted to "simplify" it away.

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.

I'm not sure if this is mentioned anywhere else, but is there a reason we cant set AVCapturePhotoSettings with a jpeg quality. Something like:

let settings = AVCapturePhotoSettings(format: [
    AVVideoCodecKey: AVVideoCodecType.jpeg,
    AVVideoCompressionPropertiesKey: [
        AVVideoQualityKey: 0.9 // quality 0-1
    ]
])

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 re-encoding approach preserves EXIF metadata and works regardless of the capture pipeline format. AVCapturePhotoSettings quality only works when explicitly capturing as JPEG.

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.

Yes, but isn't the the re-encoding approach unnecessary if the output is already set to jpeg? If someone is setting the jpeg image quality, then I would assume they would want to output a jpeg image anyways.

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.

Yeah, I guess we can safely assume that now since we also changed the method naming. Will fix!

@Bolling88
Bolling88 requested a review from hellohuanlin March 9, 2026 11:10
@bparrishMines bparrishMines added the federated: all_changes PR that contains changes for all packages for a federated plugin change label Mar 10, 2026
let savePhotoDelegate = SavePhotoDelegate(
path: path,
ioQueue: photoIOQueue,
imageQuality: fileExtension != "heif" && imageQuality < 100 ? imageQuality : nil,

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.

hmm, I dislike that imageQuality is doing any arbitrary filtering on the file extension at this layer. Could we maybe it handle it in the init?

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.

also, can we guarantee its a .heif file at this point? how about .heic files?

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.

Good points, both of you!

I've changed the check to use the fileFormat enum directly (fileFormat == .jpeg) instead of comparing extension strings. This avoids string-matching and makes the intent clear — and also addresses the .heic concern since we're no longer comparing file extensions at all.

The filtering stays at the caller level rather than moving into SavePhotoDelegate's init because the delegate shouldn't need to know about file formats — it just receives optional quality and re-encodes if set. This keeps SavePhotoDelegate focused on the save/encode responsibility while the caller decides when re-encoding is appropriate.

Regarding .heic — the format decision is made earlier in this method via the fileFormat enum (line 718). The extension is always set by us: either "heif" or "jpg". There are no .heic files in this flow.

@Bolling88
Bolling88 requested a review from LouiseHsu March 12, 2026 07:32

@camsim99 camsim99 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.

Reviewed camera_android and camera_android_camerax. Both LGTM overall, thanks for implementing this feature! Just left a couple of requests concerning a default value and some tests.


/** Controls the JPEG compression quality on the {@link android.hardware.camera2} API. */
public class JpegQualityFeature extends CameraFeature<Integer> {
private int currentSetting = 100;

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 currentSetting reflect the actual current setting by making the default requestBuilder.get(CaptureRequest.JPEG_QUALITY) instead of 100?

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.

Good question! The feature doesn't have access to a CaptureRequest.Builder at construction time — the builder is only passed to updateBuilder() during capture. Also, the feature is lazily created only when setImageQuality is first called, at which point setValue() is called immediately with the user's requested quality, so the default of 100 is never actually applied to the builder.

That said, 100 as the default makes sense semantically — it means "maximum quality" which is the expected behavior when quality hasn't been explicitly set.

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.

SGTM, thanks for the clarity :)

@Bolling88
Bolling88 requested a review from camsim99 March 17, 2026 07:08

@camsim99 camsim99 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.

Android implementations LGTM!


/** Controls the JPEG compression quality on the {@link android.hardware.camera2} API. */
public class JpegQualityFeature extends CameraFeature<Integer> {
private int currentSetting = 100;

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.

SGTM, thanks for the clarity :)

@LouiseHsu LouiseHsu 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.

lgtm on the ios side! Thanks for making those changes!

@LouiseHsu LouiseHsu added CICD Run CI/CD and removed CICD Run CI/CD labels Mar 17, 2026
@LouiseHsu

Copy link
Copy Markdown
Contributor

@Bolling88 looks like you still have some temporary dependency_overrides in the pubspec yamls - once you remove those we can probably merge :)

guard let originalData = photo.fileDataRepresentation() else {
return nil
}
return Self.reencodeJPEG(data: originalData, quality: quality)

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.

This seems to be quite brittle and could very well surprise us down the road. The imageQuality naming suggest that it could be extended to other formats such as HEIF, as you said previously, but in code we are making the assumption that it must be JPEG.

mutableData as CFMutableData,
"public.jpeg" as CFString,
1,
nil)

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.

Sorry i meant params of CGImageDestinationCreateWithData (e.g. what does nil mean?)

return data
}

return mutableData as Data

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.

This is worth documenting, in case of future cleanup that "simplifies" it into a bad state. Could you add a comment?

creatorpiyush pushed a commit to creatorpiyush/packages that referenced this pull request Jun 10, 2026
…1454)

Platform interface changes for adding JPEG compression quality control to the camera plugin. This is the platform interface subset of flutter#11155, split out per the federated plugin contribution process.

Fixes flutter/flutter#183229

## Pre-Review Checklist

[^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.
Istiak-Ahmed78 pushed a commit to Istiak-Ahmed78/packages that referenced this pull request Jun 19, 2026
…1454)

Platform interface changes for adding JPEG compression quality control to the camera plugin. This is the platform interface subset of flutter#11155, split out per the federated plugin contribution process.

Fixes flutter/flutter#183229

## Pre-Review Checklist

[^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.
@stuartmorgan-g

Copy link
Copy Markdown
Collaborator

From triage: This is just waiting on #11616 to get final updates so that it can land, then this can be reconciled and landed.

auto-submit Bot pushed a commit that referenced this pull request Jul 6, 2026
…JpegImageQuality (#11616)

Implements the `setJpegImageQuality` API (landed in the platform interface via #11454) across all camera platform implementation packages, allowing control of JPEG compression quality (1-100) for still image capture.

This is the follow-up implementation PR as requested in #11155; it does not include `camera_platform_interface` changes. The app-facing `camera` package will be submitted in a separate PR after these implementation packages are published.

Packages changed:
- `camera_android`
- `camera_android_camerax`
- `camera_avfoundation`

Part of flutter/flutter#183229

## Pre-Review Checklist

[^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.
@stuartmorgan-g

Copy link
Copy Markdown
Collaborator

Once #11616 autopublishes this can be reconciled down to just the app-facing package without dependency overrides, for final approval and landing.

@Bolling88

Copy link
Copy Markdown
Contributor Author

Reconciled down to just the app-facing camera package now that the platform implementations have been published. I also removed the dependency overrides and set the constraints to the versions that implement it

throw ArgumentError.value(quality, 'quality', 'Must be between 1 and 100.');
}
try {
await CameraPlatform.instance.setJpegImageQuality(_cameraId, quality);

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.

@bparrishMines I just looked at the platform interface PR and the default implementation throws an unimplemented error, but there's no support query API. That's not a valid combination for new APIs, because there's no way for a client to know if it's safe to call this method.

Either the default needs to be a no-op, or there needs to be a support query that's plumbed through this layer, with clear docs about checking before calling this.

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.

Went with the no-op default. Opened #12123 to change it in the platform interface. Once that lands and publishes I'll bump the constraint here to ^2.13.1 and add a doc note that it's best-effort and ignored where it's not supported. Sounds good?

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.

@bparrishMines I just looked at the platform interface PR and the default implementation throws an unimplemented error, but there's no support query API. That's not a valid combination for new APIs, because there's no way for a client to know if it's safe to call this method.

I agree. I didn't want to add an API to query for it because I expected this to be a feature that could be implemented on each platform. (e.g. like adding an API query for takePicture). But I suppose this is the downside of not implementing it on all platforms initially.

Assuming we don't have plans to implement on windows or web in the near future, an unimplemented error or a noop are neither ideal solutions. UnimplementedError would be annoying for windows/web users, while a noop could potentially lead to confusion.

I would prefer that we go with the noop and create an issue to add support for web and windows. Then add the link to the issue in the app-facing plugin so that users can check when support has been added. Therefore the documentation won't cause confusion if we forget to update it after adding support.

kalyujniy pushed a commit to brickit-app/camera that referenced this pull request Jul 8, 2026
…JpegImageQuality (flutter#11616)

Implements the `setJpegImageQuality` API (landed in the platform interface via flutter#11454) across all camera platform implementation packages, allowing control of JPEG compression quality (1-100) for still image capture.

This is the follow-up implementation PR as requested in flutter#11155; it does not include `camera_platform_interface` changes. The app-facing `camera` package will be submitted in a separate PR after these implementation packages are published.

Packages changed:
- `camera_android`
- `camera_android_camerax`
- `camera_avfoundation`

Part of flutter/flutter#183229

[^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.
auto-submit Bot pushed a commit that referenced this pull request Jul 9, 2026
…12123)

The default implementation of `setJpegImageQuality` threw `UnimplementedError`, but there is no support-query API for it, so a client has no way to know whether it is safe to call (raised in review of #11155). JPEG quality is a best-effort setting, so this changes the default to a no-op: platforms that support it override the method, and platforms that don't (e.g. web) simply ignore it.
@Piinks

Piinks commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Hey @Bolling88 are you still planning to work on this change?

@Bolling88

Copy link
Copy Markdown
Contributor Author

Hey @Bolling88 are you still planning to work on this change?

Yes, still on it.

I've rebased onto current main, so the merge conflict is gone, and scoped this PR down to just the app-facing camera package now that the platform interface and the implementations have published.

The two open review points are handled:

The diff is now 5 files in packages/camera/camera only, no dependency overrides.

This needs a re-review from @stuartmorgan-g to clear the changes-requested, and the CICD label for full presubmit.

@stuartmorgan-g stuartmorgan-g added autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD labels Aug 26, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Aug 26, 2026
@auto-submit

auto-submit Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/packages/11155, because - The status or check suite Dashboard Checks has failed. Please fix the issues identified (or deflake) before re-applying this label.

@stuartmorgan-g

Copy link
Copy Markdown
Collaborator

The camera min Dart and Flutter SDK versions will need to be updated to match the versions required by the latest camera_android_camerax to resolve the CI failure.

@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Aug 27, 2026
Adds `CameraController.setJpegImageQuality(int quality)` to the app-facing
camera package, forwarding to the platform interface method added in
camera_platform_interface 2.13.0 and implemented in camera_android_camerax
0.7.4 and camera_avfoundation 0.10.2.

The default platform interface implementation is a no-op (2.13.1), so the
dartdoc documents this as a best-effort setting and links to
flutter/flutter#191790 for the current state of
platform support.

Fixes flutter/flutter#183229
The bumped camera_android_camerax constraint requires Flutter 3.44/Dart 3.12,
so camera has to declare the same minimum for the legacy analysis jobs to skip
it. Raising the language version surfaces prefer_initializing_formals on the
CameraValue constructor; an initializing formal is not possible there because
named parameters cannot be private, so the lint is ignored with a reason.
@Bolling88

Copy link
Copy Markdown
Contributor Author

Rebased onto latest main, so the branch is up to date now. The min SDK bump to Flutter 3.44/Dart 3.12 is in.

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

Labels

federated: all_changes PR that contains changes for all packages for a federated plugin change p: camera

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proposal:[camera] Add API to control JPEG compression quality

7 participants