From 3000d17b6901c947955c3106085638bc1f453baa Mon Sep 17 00:00:00 2001 From: Yashas H Majmudar Date: Tue, 2 Jun 2026 19:06:17 -0400 Subject: [PATCH 1/5] fix: handle limit 1 in pickMultiImage and pickMultipleMedia gracefully --- .../image_picker/lib/image_picker.dart | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/image_picker/image_picker/lib/image_picker.dart b/packages/image_picker/image_picker/lib/image_picker.dart index 95fcd19120b9..c8604a617769 100755 --- a/packages/image_picker/image_picker/lib/image_picker.dart +++ b/packages/image_picker/image_picker/lib/image_picker.dart @@ -133,6 +133,18 @@ class ImagePicker { int? limit, bool requestFullMetadata = true, }) { + // limit: 1 would fail MultiImagePickerOptions validation (requires >= 2), + // so delegate to pickImage which already handles single-image selection. + if (limit == 1) { + return pickImage( + source: ImageSource.gallery, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + requestFullMetadata: requestFullMetadata, + ).then((XFile? image) => image == null ? [] : [image]); + } + final imageOptions = ImageOptions.createAndValidate( maxWidth: maxWidth, maxHeight: maxHeight, @@ -249,6 +261,17 @@ class ImagePicker { int? limit, bool requestFullMetadata = true, }) { + // limit: 1 would fail MediaOptions validation (requires >= 2), + // so delegate to pickMedia which already handles single-item selection. + if (limit == 1) { + return pickMedia( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + requestFullMetadata: requestFullMetadata, + ).then((XFile? media) => media == null ? [] : [media]); + } + return platform.getMedia( options: MediaOptions.createAndValidate( allowMultiple: true, From d9412d2b613bd15bb12afd4071a4de74f9515f68 Mon Sep 17 00:00:00 2001 From: Yashas H Majmudar Date: Tue, 2 Jun 2026 19:06:36 -0400 Subject: [PATCH 2/5] add: tests to verify deligation to pick single function --- .../image_picker/test/image_picker_test.dart | 98 ++++++++++++++++++- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/packages/image_picker/image_picker/test/image_picker_test.dart b/packages/image_picker/image_picker/test/image_picker_test.dart index 6230481169f3..e21c3964e7fd 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.dart @@ -709,13 +709,55 @@ void main() { ); }); - test('does not accept a limit argument lower than 2', () { + test('does not accept a limit argument lower than 1', () { final picker = ImagePicker(); expect(() => picker.pickMultiImage(limit: -1), throwsArgumentError); expect(() => picker.pickMultiImage(limit: 0), throwsArgumentError); + }); + + test('delegates to pickImage when limit is 1 and image is picked', + () async { + when( + mockPlatform.getImageFromSource( + source: anyNamed('source'), + options: anyNamed('options'), + ), + ).thenAnswer( + (Invocation _) async => XFile('test_path', name: 'test.jpg')); + + final picker = ImagePicker(); + final List result = await picker.pickMultiImage(limit: 1); + + expect(result, hasLength(1)); + expect(result.first.path, 'test_path'); + verify( + mockPlatform.getImageFromSource( + source: ImageSource.gallery, + options: anyNamed('options'), + ), + ); + }); + + test('delegates to pickImage when limit is 1 and no image is picked', + () async { + when( + mockPlatform.getImageFromSource( + source: anyNamed('source'), + options: anyNamed('options'), + ), + ).thenAnswer((Invocation _) async => null); - expect(() => picker.pickMultiImage(limit: 1), throwsArgumentError); + final picker = ImagePicker(); + final List result = await picker.pickMultiImage(limit: 1); + + expect(result, isEmpty); + verify( + mockPlatform.getImageFromSource( + source: ImageSource.gallery, + options: anyNamed('options'), + ), + ); }); test('handles an empty image file response gracefully', () async { @@ -1104,7 +1146,7 @@ void main() { ); }); - test('does not accept a limit argument lower than 2', () { + test('does not accept a limit argument lower than 1', () { final picker = ImagePicker(); expect( () => picker.pickMultipleMedia(limit: -1), @@ -1112,8 +1154,56 @@ void main() { ); expect(() => picker.pickMultipleMedia(limit: 0), throwsArgumentError); + }); + + test('delegates to pickMedia when limit is 1 and media is picked', + () async { + when( + mockPlatform.getMedia(options: anyNamed('options')), + ).thenAnswer( + (Invocation _) async => [XFile('test_path')]); + + final picker = ImagePicker(); + final List result = await picker.pickMultipleMedia(limit: 1); + + expect(result, hasLength(1)); + expect(result.first.path, 'test_path'); + verify( + mockPlatform.getMedia( + options: argThat( + isInstanceOf().having( + (MediaOptions options) => options.allowMultiple, + 'allowMultiple', + isFalse, + ), + named: 'options', + ), + ), + ); + }); + + test('delegates to pickMedia when limit is 1 and no media is picked', + () async { + when( + mockPlatform.getMedia(options: anyNamed('options')), + ).thenAnswer((Invocation _) async => []); - expect(() => picker.pickMultipleMedia(limit: 1), throwsArgumentError); + final picker = ImagePicker(); + final List result = await picker.pickMultipleMedia(limit: 1); + + expect(result, isEmpty); + verify( + mockPlatform.getMedia( + options: argThat( + isInstanceOf().having( + (MediaOptions options) => options.allowMultiple, + 'allowMultiple', + isFalse, + ), + named: 'options', + ), + ), + ); }); test('handles an empty image file response gracefully', () async { From a06f29065a5109d16b67558040b4a3ce1bb54ab8 Mon Sep 17 00:00:00 2001 From: Yashas H Majmudar Date: Tue, 2 Jun 2026 19:06:54 -0400 Subject: [PATCH 3/5] update: changelog with description --- packages/image_picker/image_picker/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 6b19209defc7..499581bcd71f 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,5 +1,8 @@ ## NEXT +* Fixes `pickMultiImage(limit: 1)` throwing an `ArgumentError` by delegating + to `pickImage` when `limit` is exactly 1, since the platform interface + requires `limit >= 2` for multi-image selection. * Updates minimum supported SDK version to Flutter 3.38/Dart 3.10. ## 1.2.2 From 2432cba8e6029e16998f31619ec9c90e0592ba91 Mon Sep 17 00:00:00 2001 From: Yashas H Majmudar Date: Tue, 2 Jun 2026 19:13:09 -0400 Subject: [PATCH 4/5] chore: dart format --- .../image_picker/test/image_picker_test.dart | 170 +++++++++--------- 1 file changed, 89 insertions(+), 81 deletions(-) diff --git a/packages/image_picker/image_picker/test/image_picker_test.dart b/packages/image_picker/image_picker/test/image_picker_test.dart index e21c3964e7fd..91dfedfa0f39 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.dart @@ -716,49 +716,54 @@ void main() { expect(() => picker.pickMultiImage(limit: 0), throwsArgumentError); }); - test('delegates to pickImage when limit is 1 and image is picked', - () async { - when( - mockPlatform.getImageFromSource( - source: anyNamed('source'), - options: anyNamed('options'), - ), - ).thenAnswer( - (Invocation _) async => XFile('test_path', name: 'test.jpg')); - - final picker = ImagePicker(); - final List result = await picker.pickMultiImage(limit: 1); + test( + 'delegates to pickImage when limit is 1 and image is picked', + () async { + when( + mockPlatform.getImageFromSource( + source: anyNamed('source'), + options: anyNamed('options'), + ), + ).thenAnswer( + (Invocation _) async => XFile('test_path', name: 'test.jpg'), + ); - expect(result, hasLength(1)); - expect(result.first.path, 'test_path'); - verify( - mockPlatform.getImageFromSource( - source: ImageSource.gallery, - options: anyNamed('options'), - ), - ); - }); + final picker = ImagePicker(); + final List result = await picker.pickMultiImage(limit: 1); + + expect(result, hasLength(1)); + expect(result.first.path, 'test_path'); + verify( + mockPlatform.getImageFromSource( + source: ImageSource.gallery, + options: anyNamed('options'), + ), + ); + }, + ); - test('delegates to pickImage when limit is 1 and no image is picked', - () async { - when( - mockPlatform.getImageFromSource( - source: anyNamed('source'), - options: anyNamed('options'), - ), - ).thenAnswer((Invocation _) async => null); + test( + 'delegates to pickImage when limit is 1 and no image is picked', + () async { + when( + mockPlatform.getImageFromSource( + source: anyNamed('source'), + options: anyNamed('options'), + ), + ).thenAnswer((Invocation _) async => null); - final picker = ImagePicker(); - final List result = await picker.pickMultiImage(limit: 1); + final picker = ImagePicker(); + final List result = await picker.pickMultiImage(limit: 1); - expect(result, isEmpty); - verify( - mockPlatform.getImageFromSource( - source: ImageSource.gallery, - options: anyNamed('options'), - ), - ); - }); + expect(result, isEmpty); + verify( + mockPlatform.getImageFromSource( + source: ImageSource.gallery, + options: anyNamed('options'), + ), + ); + }, + ); test('handles an empty image file response gracefully', () async { final picker = ImagePicker(); @@ -1156,55 +1161,58 @@ void main() { expect(() => picker.pickMultipleMedia(limit: 0), throwsArgumentError); }); - test('delegates to pickMedia when limit is 1 and media is picked', - () async { - when( - mockPlatform.getMedia(options: anyNamed('options')), - ).thenAnswer( - (Invocation _) async => [XFile('test_path')]); - - final picker = ImagePicker(); - final List result = await picker.pickMultipleMedia(limit: 1); + test( + 'delegates to pickMedia when limit is 1 and media is picked', + () async { + when( + mockPlatform.getMedia(options: anyNamed('options')), + ).thenAnswer((Invocation _) async => [XFile('test_path')]); - expect(result, hasLength(1)); - expect(result.first.path, 'test_path'); - verify( - mockPlatform.getMedia( - options: argThat( - isInstanceOf().having( - (MediaOptions options) => options.allowMultiple, - 'allowMultiple', - isFalse, + final picker = ImagePicker(); + final List result = await picker.pickMultipleMedia(limit: 1); + + expect(result, hasLength(1)); + expect(result.first.path, 'test_path'); + verify( + mockPlatform.getMedia( + options: argThat( + isInstanceOf().having( + (MediaOptions options) => options.allowMultiple, + 'allowMultiple', + isFalse, + ), + named: 'options', ), - named: 'options', ), - ), - ); - }); - - test('delegates to pickMedia when limit is 1 and no media is picked', - () async { - when( - mockPlatform.getMedia(options: anyNamed('options')), - ).thenAnswer((Invocation _) async => []); + ); + }, + ); - final picker = ImagePicker(); - final List result = await picker.pickMultipleMedia(limit: 1); + test( + 'delegates to pickMedia when limit is 1 and no media is picked', + () async { + when( + mockPlatform.getMedia(options: anyNamed('options')), + ).thenAnswer((Invocation _) async => []); - expect(result, isEmpty); - verify( - mockPlatform.getMedia( - options: argThat( - isInstanceOf().having( - (MediaOptions options) => options.allowMultiple, - 'allowMultiple', - isFalse, + final picker = ImagePicker(); + final List result = await picker.pickMultipleMedia(limit: 1); + + expect(result, isEmpty); + verify( + mockPlatform.getMedia( + options: argThat( + isInstanceOf().having( + (MediaOptions options) => options.allowMultiple, + 'allowMultiple', + isFalse, + ), + named: 'options', ), - named: 'options', ), - ), - ); - }); + ); + }, + ); test('handles an empty image file response gracefully', () async { final picker = ImagePicker(); From 98e1a166884f24a8e82377f7fa5f6b289de09b4c Mon Sep 17 00:00:00 2001 From: Yashas H Majmudar Date: Tue, 2 Jun 2026 19:20:53 -0400 Subject: [PATCH 5/5] chore: update changelog --- packages/image_picker/image_picker/CHANGELOG.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 499581bcd71f..0c77264a14e3 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,8 +1,7 @@ ## NEXT -* Fixes `pickMultiImage(limit: 1)` throwing an `ArgumentError` by delegating - to `pickImage` when `limit` is exactly 1, since the platform interface - requires `limit >= 2` for multi-image selection. +* Fixes `pickMultiImage(limit: 1)` and `pickMultipleMedia(limit: 1)` throwing an `ArgumentError` by delegating to + single-item pickers when `limit` is exactly 1, since the platform interface requires `limit >= 2` for multi-selection. * Updates minimum supported SDK version to Flutter 3.38/Dart 3.10. ## 1.2.2