Skip to content

[image_picker_ios] PR 4/5 Migrate the view provider, PHPicker save operation, and image utilities to Swift - #13394

Merged
auto-submit[bot] merged 6 commits into
flutter:mainfrom
victogomez-cs:pr4/image-picker-ios-view-provider-and-image-utils
Oct 29, 2026
Merged

auto-submit[bot] merged 6 commits into
flutter:mainfrom
victogomez-cs:pr4/image-picker-ios-view-provider-and-image-utils

Conversation

@victogomez-cs

@victogomez-cs victogomez-cs commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Migrates the Objective-C helpers that ImagePickerPlugin calls into the Swift target:

  • FIPViewProvider becomes ViewProvider / DefaultViewProvider.
  • FLTPHPickerSaveImageToPathOperation becomes PHPickerSaveImageToPathOperation.
  • FLTImagePickerImageUtil, FLTImagePickerMetaDataUtil, and FLTImagePickerPhotoAssetUtil become ImagePickerImageUtil, ImagePickerMetaDataUtil, and ImagePickerPhotoAssetUtil.
  • The PHImageManager seam becomes ImageDataRequesting.
  • PHPickerResult conforms to PickerItem, replacing the BridgedPickerItem wrapper.

Deletes the custom module map and image_picker_ios.h umbrella header. The image_picker_ios_objc target remains for the Pigeon messages until the next PR. CocoaPods no longer sets s.module_map.

Bumps image_picker_ios to 0.8.13+14.

PR 4/5 of the Obj-C → Swift migration, stacked on #13235. Part of flutter/flutter#119107

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 Oct 22, 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 migrates the iOS implementation of the image_picker plugin from Objective-C to Swift, restructuring the codebase into mixed-language Swift Package Manager targets and updating the corresponding unit tests. Feedback on the changes highlights several critical improvements: ensuring thread safety by dispatching Flutter result callbacks on the main thread, avoiding potential crashes by safely copying immutable GIF metadata dictionaries before modification, and safely identifying video type identifiers. Additionally, several suggestions were made to align the Swift code with idiomatic practices, such as removing redundant synchronization, avoiding unnecessary force-unwrapping, and simplifying redundant helper calls.

Comment on lines +126 to +127
let typeIdentifier = result.itemProvider.registeredTypeIdentifiers.first
result.itemProvider.loadFileRepresentation(forTypeIdentifier: typeIdentifier ?? "") {

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.

high

Using registeredTypeIdentifiers.first to load the video representation can be problematic if the first identifier is an image (e.g., for Live Photos or assets with multiple representations). It is safer to explicitly find the first identifier that conforms to the movie type.

    let typeIdentifier = result.itemProvider.registeredTypeIdentifiers.first { identifier in
      UTType(identifier)?.conforms(to: .movie) ?? false
    } ?? UTType.movie.identifier
    result.itemProvider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) {

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.

processVideo only runs after the item has failed the image check and matched UTType.movie. This uses registeredTypeIdentifiers.first, which is the Swift form of the Objective-C firstObject. Live Photos take the image branch before this method runs, so I'm leaving the identifier selection as ported

Comment on lines +111 to +114
let gifProperties =
gifMetaProperties[kCGImagePropertyGIFDictionary] as? NSMutableDictionary
// Messaging a nil gifProperties is a no-op in Objective-C; keep that behavior.
gifProperties?[kCGImagePropertyGIFLoopCount] = 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.

high

Casting gifMetaProperties[kCGImagePropertyGIFDictionary] directly to NSMutableDictionary using as? NSMutableDictionary will fail for immutable dictionaries (which is what metaData usually contains). This prevents setting the loop count to 0. Creating a mutable copy of the dictionary resolves this issue.

Suggested change
let gifProperties =
gifMetaProperties[kCGImagePropertyGIFDictionary] as? NSMutableDictionary
// Messaging a nil gifProperties is a no-op in Objective-C; keep that behavior.
gifProperties?[kCGImagePropertyGIFLoopCount] = 0
if let existingGifProperties = gifMetaProperties[kCGImagePropertyGIFDictionary] as? NSDictionary,
let gifProperties = existingGifProperties.mutableCopy() as? NSMutableDictionary {
gifProperties[kCGImagePropertyGIFLoopCount] = 0
gifMetaProperties[kCGImagePropertyGIFDictionary] = gifProperties
}

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. ImageIO's nested GIF dictionary is immutable, so as? NSMutableDictionary was nil and the loop count was never written. The dictionary is copied, the loop count is set to 0, and that copy is written back. The GIF save tests expect that loop count

Comment on lines +619 to +644
func sendCallResult(withSavedPathList pathList: [String?]?) {
guard let callContext else {
return
}

if let pathList, pathList.contains(where: { $0 == nil }) {
callContext.result(
nil,
FlutterError(
code: "create_error",
message: "pathList's items should not be null",
details: nil))
} else {
let paths = pathList?.compactMap { $0 } ?? []
callContext.result(paths, nil)
}
self.callContext = nil
}

func sendCallResult(with error: FlutterError) {
guard let callContext else {
return
}
callContext.result(nil, error)
self.callContext = 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.

high

The sendCallResult methods can be invoked from background threads (e.g., when retrieving full metadata via requestImageDataAndOrientation). Invoking Flutter's result callback on a background thread violates Flutter's threading model and can cause crashes or undefined behavior. Wrapping the bodies of both sendCallResult methods in DispatchQueue.main.async ensures thread safety.

  func sendCallResult(withSavedPathList pathList: [String?]?) {
    DispatchQueue.main.async {
      guard let callContext = self.callContext else {
        return
      }

      if let pathList, pathList.contains(where: { $0 == nil }) {
        callContext.result(
          nil,
          FlutterError(
            code: "create_error",
            message: "pathList's items should not be null",
            details: nil))
      } else {
        let paths = pathList?.compactMap { $0 } ?? []
        callContext.result(paths, nil)
      }
      self.callContext = nil
    }
  }

  func sendCallResult(with error: FlutterError) {
    DispatchQueue.main.async {
      guard let callContext = self.callContext else {
        return
      }
      callContext.result(nil, error)
      self.callContext = nil
    }
  }

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.

This method is unchanged from the Swift plugin already on main. This PR only renamed the utility calls around it. The unit tests rely on sendCallResult invoking the callback synchronously on the calling thread. Wrapping it in DispatchQueue.main.async would change that for every picker result, so I'm not doing it in this migration

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 sounds like an issue. When you run it, is it on main thread? If not, can you add a TODO in the 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.

The picker delegate and the PHPicker completion operation run on the main queue. requestImageDataAndOrientation's result handler is documented to run on an arbitrary queue, and that path calls sendCallResult when full metadata is requested. I added a TODO there. I left the callback synchronous so the unit tests can still read it on the calling thread

Comment on lines +332 to +337
objc_sync_enter(self)
let beingPresented = imagePickerController.isBeingPresented
objc_sync_exit(self)
if beingPresented {
return
}

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.

medium

Using objc_sync_enter(self) / objc_sync_exit(self) is unnecessary and unidiomatic in Swift here. Since showCamera is always called on the main thread, synchronization is redundant.

Suggested change
objc_sync_enter(self)
let beingPresented = imagePickerController.isBeingPresented
objc_sync_exit(self)
if beingPresented {
return
}
if imagePickerController.isBeingPresented {
return
}

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.

This is the port of the Objective-C @synchronized(self) around isBeingPresented. The permission callback already hops to the main queue before calling showCamera, and I'm keeping the same lock the previous implementation had

Comment on lines +25 to +28
if imageToScale == nil || width == 0 || height == 0 {
return nil
}
let imageToScale = imageToScale!

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.

medium

Force-unwrapping imageToScale! after checking for nil is unidiomatic in Swift. Using guard let is safer and cleaner.

Suggested change
if imageToScale == nil || width == 0 || height == 0 {
return nil
}
let imageToScale = imageToScale!
guard let imageToScale, width > 0, height > 0 else {
return nil
}

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. drawScaledImage now uses guard let and returns nil when the image is nil or either dimension is zero, matching the Objective-C check

Comment on lines +92 to +94
guard let image, let cgImage = image.cgImage else {
return drawScaledImage(nil, width: width, height: height)
}

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.

medium

Calling drawScaledImage(nil, ...) when image or cgImage is nil is redundant because drawScaledImage always returns nil when its first argument is nil. Returning nil directly is cleaner and more efficient.

Suggested change
guard let image, let cgImage = image.cgImage else {
return drawScaledImage(nil, width: width, height: height)
}
guard let image, let cgImage = image.cgImage else {
return nil
}

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. A missing image or cgImage returns nil directly

@victogomez-cs
victogomez-cs force-pushed the pr4/image-picker-ios-view-provider-and-image-utils branch from 81d3f3e to 5112655 Compare October 22, 2026 21:47
The generated Swift header still imports the umbrella while Pigeon remains Objective-C, and 0.8.13+13 is already on main.
@victogomez-cs victogomez-cs added the triage-ios Should be looked at in iOS triage label Oct 22, 2026
This update ensures animated GIFs loop correctly when the source metadata is immutable. Additionally, it refines the image scaling logic by eliminating unnecessary force unwrapping and improving nil handling in the image drawing function.
@LongCatIsLooong
LongCatIsLooong self-requested a review October 23, 2026 22:00
@jmagman
jmagman requested review from hellohuanlin and removed request for LongCatIsLooong October 28, 2026 17:56

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

Overall looks good. Left some comments

(originalMetaDataJPG["ProfileName"] as? String)
== (newMetaDataJPG["ProfileName"] as? String)
(originalMetaDataJPG?["ProfileName"] as? String)
== (newMetaDataJPG?["ProfileName"] as? String)

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.

just double check - if both are nil, should the test pass or fail? is nil metadata expected or unexpected?

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.

Nil is unexpected. Both fixtures have ProfileName sRGB IEC61966-2.1, but nil == nil used to pass. The test now requires the original profile name and still checks that the saved file has the same name. I made the same change for the PNG assertion below


/// A `PickerItem` stand-in that does not require constructing a `PHPickerResult`.
final class FakePickerItem: NSObject, PickerItem {
@available(iOS 14, *)

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.

You can delete this since image picker is ios 15+:

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.

Removed. The package deployment target is iOS 15, so @available(iOS 14, *) on FakePickerItem is redundant

for asset: PHAsset,
options: PHImageRequestOptions?,
resultHandler:
@escaping (Data?, String?, CGImagePropertyOrientation, [AnyHashable: Any]?) -> Void

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 getting a bit long, can you typealias it and provide with parameter name for readability

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. The handler is now ImageDataHandler, with names for the image data, UTI, orientation, and info dictionary

private static func drawScaledImage(_ imageToScale: UIImage?, width: Double, height: Double)
-> UIImage?
{
guard let imageToScale, width != 0, height != 0 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.

> 0

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. drawScaledImage now returns nil unless width > 0 and height > 0


private let kFirstByteJPEG: UInt8 = 0xFF
private let kFirstBytePNG: UInt8 = 0x89
private let kFirstByteGIF: UInt8 = 0x47

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 know this is just migration, but is there any documentation or source for these values?

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.

These are the first bytes of the file signatures, carried over from the Objective-C constants. JPEG's SOI marker is 0xFFD8, PNG's signature starts with 0x89, and GIF's header starts with ASCII G (0x47). I added that above the constants

if quality != nil && type != .jpeg {
let suffix = imageTypeSuffix(from: type) ?? ""
NSLog(
"image_picker: compressing is not supported for type %@. Returning the image with original 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 log also from objc? should this be reported to dart side?

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, this is the same NSLog from FLTImagePickerMetaDataUtil. It is not reported to Dart. For a non-JPEG type the image is still returned, PNG data as-is and other types as JPEG, and the caller saves that and returns the path. I would not add a new Dart error in this migration unless you want that behavior change

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.

Let's leave it but add a TODO then. This kind of error should be surfaced to developers so they can handle it.

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. I left the NSLog and added a TODO

Comment on lines +619 to +644
func sendCallResult(withSavedPathList pathList: [String?]?) {
guard let callContext else {
return
}

if let pathList, pathList.contains(where: { $0 == nil }) {
callContext.result(
nil,
FlutterError(
code: "create_error",
message: "pathList's items should not be null",
details: nil))
} else {
let paths = pathList?.compactMap { $0 } ?? []
callContext.result(paths, nil)
}
self.callContext = nil
}

func sendCallResult(with error: FlutterError) {
guard let callContext else {
return
}
callContext.result(nil, error)
self.callContext = 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.

This sounds like an issue. When you run it, is it on main thread? If not, can you add a TODO in the comment?

let suffix = imageTypeSuffix(from: type) ?? ""
// TODO: Surface this to Dart so callers can handle an unsupported
// compression request. This stays an `NSLog`, matching
// `FLTImagePickerMetaDataUtil`, and the image is still returned.

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 remove This stays an NSLog, matching FLTImagePickerMetaDataUtil, and the image is still returned. - after migration, this class is gone, and the comment would be confusing

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. Removed that sentence

@hellohuanlin hellohuanlin added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 29, 2026
@auto-submit
auto-submit Bot merged commit 09e67ff into flutter:main Oct 29, 2026
13 checks passed
@victogomez-cs
victogomez-cs deleted the pr4/image-picker-ios-view-provider-and-image-utils branch October 29, 2026 21:54
flutter-zl pushed a commit to flutter-zl/flutter that referenced this pull request Oct 30, 2026
flutter/packages@0b72e92...3742a4e

2026-10-30 [email protected] [pigeon] Allow event channel
unregistration (flutter/packages#13438)
2026-10-30 [email protected] [material_ui] Fix Escape key not
closing MenuAnchor opened by tap or click (flutter/packages#13430)
2026-10-30 [email protected] manual roll and manual build and copy
(flutter/packages#13495)
2026-10-29 [email protected] [pigeon] Separate native interop Dart
bindings into a conditionally imported companion file
(flutter/packages#13393)
2026-10-29 [email protected] [image_picker_ios] PR 4/5 Migrate
the view provider, PHPicker save operation, and image utilities to Swift
(flutter/packages#13394)
2026-10-29 [email protected] Sync release-material_ui-1.9.0 to
main (flutter/packages#13479)
2026-10-29 [email protected] [material_ui]
Unskip web tests that pass on CanvasKit and Skwasm: time_picker_test
(flutter/packages#13471)
2026-10-29 [email protected] [vector_graphics]
Dispose pictures decoded after their VectorGraphic is removed
(flutter/packages#13435)
2026-10-29 [email protected]
[camera_android_x] implement CameraX RecordingStats and VideoRecordEvent
APIs (flutter/packages#13330)
2026-10-29 [email protected] Sync release-go_router-18.1.2 to
main (flutter/packages#13480)
2026-10-29 [email protected] Sync release-cupertino_ui-1.1.5 to
main (flutter/packages#13481)
2026-10-29 [email protected]
[vector_graphics_compiler] Fix em units in text positions
(flutter/packages#13388)

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 [email protected] 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
gmackall pushed a commit to gmackall/flutter that referenced this pull request Nov 3, 2026
flutter/packages@0b72e92...3742a4e

2026-10-30 [email protected] [pigeon] Allow event channel
unregistration (flutter/packages#13438)
2026-10-30 [email protected] [material_ui] Fix Escape key not
closing MenuAnchor opened by tap or click (flutter/packages#13430)
2026-10-30 [email protected] manual roll and manual build and copy
(flutter/packages#13495)
2026-10-29 [email protected] [pigeon] Separate native interop Dart
bindings into a conditionally imported companion file
(flutter/packages#13393)
2026-10-29 [email protected] [image_picker_ios] PR 4/5 Migrate
the view provider, PHPicker save operation, and image utilities to Swift
(flutter/packages#13394)
2026-10-29 [email protected] Sync release-material_ui-1.9.0 to
main (flutter/packages#13479)
2026-10-29 [email protected] [material_ui]
Unskip web tests that pass on CanvasKit and Skwasm: time_picker_test
(flutter/packages#13471)
2026-10-29 [email protected] [vector_graphics]
Dispose pictures decoded after their VectorGraphic is removed
(flutter/packages#13435)
2026-10-29 [email protected]
[camera_android_x] implement CameraX RecordingStats and VideoRecordEvent
APIs (flutter/packages#13330)
2026-10-29 [email protected] Sync release-go_router-18.1.2 to
main (flutter/packages#13480)
2026-10-29 [email protected] Sync release-cupertino_ui-1.1.5 to
main (flutter/packages#13481)
2026-10-29 [email protected]
[vector_graphics_compiler] Fix em units in text positions
(flutter/packages#13388)

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 [email protected] 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
technobibi added a commit to technobibi/packages that referenced this pull request Nov 3, 2026
Resolves conflicts with flutter#13394, which replaced the Objective-C image
utilities and the PHPicker save operation with Swift:

- Moves this change to 0.8.13+15, above main's 0.8.13+14 entry.
- Re-applies the fix to the new Swift helpers: the save methods return
  nil instead of writing an empty file or returning a path that was
  never written, the GIF writer no longer force-unwraps a destination
  that could not be created, and merging metadata returns nil when
  CGImageDestinationFinalize fails. The PHPicker operation and both
  UIImagePickerController paths report `invalid_image` when saving
  fails.
- Makes the path-taking file and GIF writers internal so the tests can
  pass a path that cannot be written.
auto-submit Bot pushed a commit that referenced this pull request Nov 5, 2026
…tive-C to Swift migration. (#13493)

Switches `image_picker_ios` Pigeon output from Objective-C to Swift and removes the remaining Objective-C packaging:

* `pigeons/messages.dart` now generates `messages.g.swift` instead of `messages.g.h` and `messages.g.m`.
* `ImagePickerPlugin` and the PHPicker save operation use the generated Swift API, including `PigeonError` instead of `FlutterError`.
* Deletes the `image_picker_ios_objc` Swift Package Manager target, the `image_picker_ios.h` umbrella header, and the custom module map.
* The CocoaPods pod is Swift-only and no longer sets `s.module_map`.

The iOS example app and UI tests stay Objective-C until the next PR.

Bumps image_picker_ios to **0.8.13+15**.

PR 5/5 of the Obj-C → Swift migration, following #13394. Part of flutter/flutter#119107

## Pre-Review Checklist

**Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). 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.

[^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.
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: image_picker platform-ios triage-ios Should be looked at in iOS triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants