Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/image_picker/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## NEXT
## 1.2.3

* 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
Expand Down
35 changes: 33 additions & 2 deletions packages/image_picker/image_picker/lib/image_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,23 @@ class ImagePicker {
int? imageQuality,
int? limit,
bool requestFullMetadata = true,
}) {
}) async {
if (limit != null && limit < 1) {
throw ArgumentError.value(limit, 'limit', 'cannot be lower than 1');
}
// limit: 1 would fail MultiImagePickerOptions validation (requires >= 2),
// so delegate to pickImage which already handles single-image selection.
if (limit == 1) {
Comment thread
yashas-hm marked this conversation as resolved.
final XFile? image = await pickImage(
source: ImageSource.gallery,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
requestFullMetadata: requestFullMetadata,
);
return <XFile>[if (image != null) image];
}

final imageOptions = ImageOptions.createAndValidate(
maxWidth: maxWidth,
maxHeight: maxHeight,
Expand Down Expand Up @@ -242,7 +258,22 @@ class ImagePicker {
int? imageQuality,
int? limit,
bool requestFullMetadata = true,
}) {
}) async {
if (limit != null && limit < 1) {
throw ArgumentError.value(limit, 'limit', 'cannot be lower than 1');
}
// limit: 1 would fail MediaOptions validation (requires >= 2),
// so delegate to pickMedia which already handles single-item selection.
if (limit == 1) {
Comment thread
yashas-hm marked this conversation as resolved.
final XFile? media = await pickMedia(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
requestFullMetadata: requestFullMetadata,
);
return <XFile>[if (media != null) media];
}

return platform.getMedia(
options: MediaOptions.createAndValidate(
allowMultiple: true,
Expand Down
2 changes: 1 addition & 1 deletion packages/image_picker/image_picker/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image
library, and taking new pictures with the camera.
repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22
version: 1.2.2
version: 1.2.3

environment:
sdk: ^3.10.0
Expand Down
92 changes: 88 additions & 4 deletions packages/image_picker/image_picker/test/image_picker_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -610,13 +610,52 @@ void main() {
expect(() => picker.pickMultiImage(maxHeight: -1.0), throwsArgumentError);
});

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<XFile> 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<XFile> 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 {
Expand Down Expand Up @@ -968,13 +1007,58 @@ void main() {
expect(() => picker.pickMultipleMedia(maxHeight: -1.0), throwsArgumentError);
});

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), throwsArgumentError);

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>[XFile('test_path')]);

final picker = ImagePicker();
final List<XFile> result = await picker.pickMultipleMedia(limit: 1);

expect(result, hasLength(1));
expect(result.first.path, 'test_path');
verify(
mockPlatform.getMedia(
options: argThat(
isInstanceOf<MediaOptions>().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 => <XFile>[]);

expect(() => picker.pickMultipleMedia(limit: 1), throwsArgumentError);
final picker = ImagePicker();
final List<XFile> result = await picker.pickMultipleMedia(limit: 1);

expect(result, isEmpty);
verify(
mockPlatform.getMedia(
options: argThat(
isInstanceOf<MediaOptions>().having(
(MediaOptions options) => options.allowMultiple,
'allowMultiple',
isFalse,
),
named: 'options',
),
),
);
});

test('handles an empty image file response gracefully', () async {
Expand Down
Loading