Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[image_picker_platform_interface] Added pickMultiImage #3782

Merged
merged 12 commits into from
Apr 13, 2021
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.1.0

* Add `pickMultiImage` method.

## 2.0.1

* Update platform_plugin_interface version requirement.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,54 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
return path != null ? PickedFile(path) : null;
}

@override
Future<List<PickedFile>?> pickMultiImage({
double? maxWidth,
double? maxHeight,
int? imageQuality,
}) async {
final List<dynamic>? paths = await _pickMultiImagePath(
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
);
if (paths == null) return null;

final List<PickedFile> files = [];
for (final path in paths) {
files.add(PickedFile(path));
}
return files;
}

Future<List<dynamic>?> _pickMultiImagePath({
double? maxWidth,
double? maxHeight,
int? imageQuality,
}) {
if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) {
throw ArgumentError.value(
imageQuality, 'imageQuality', 'must be between 0 and 100');
}

if (maxWidth != null && maxWidth < 0) {
throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative');
}

if (maxHeight != null && maxHeight < 0) {
throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative');
}

return _channel.invokeMethod<List<dynamic>?>(
'pickMultiImage',
<String, dynamic>{
'maxWidth': maxWidth,
'maxHeight': maxHeight,
'imageQuality': imageQuality,
},
);
}

Future<String?> _pickImagePath({
required ImageSource source,
double? maxWidth,
Expand Down Expand Up @@ -74,7 +122,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
}) async {
String? path = await _pickVideoPath(
final String? path = await _pickVideoPath(
source: source,
maxDuration: maxDuration,
preferredCameraDevice: preferredCameraDevice,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ abstract class ImagePickerPlatform extends PlatformInterface {
///
/// The `imageQuality` argument modifies the quality of the image, ranging from 0-100
/// where 100 is the original/max quality. If `imageQuality` is null, the image with
/// the original quality will be returned. Compression is only supportted for certain
/// the original quality will be returned. Compression is only supported for certain
/// image types such as JPEG. If compression is not supported for the image that is picked,
/// an warning message will be logged.
/// a warning message will be logged.
///
/// Use `preferredCameraDevice` to specify the camera to use when the `source` is [ImageSource.camera].
/// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device.
Expand All @@ -78,6 +78,32 @@ abstract class ImagePickerPlatform extends PlatformInterface {
throw UnimplementedError('pickImage() has not been implemented.');
}

/// Returns a [List<PickedFile>] with the images that were picked.
///
/// The images come from the [ImageSource.gallery].
///
/// Where iOS supports HEIC images, Android 8 and below doesn't. Android 9 and above only support HEIC images if used
/// in addition to a size modification, of which the usage is explained below.
///
/// If specified, the image will be at most `maxWidth` wide and
/// `maxHeight` tall. Otherwise the image will be returned at it's
/// original width and height.
///
/// The `imageQuality` argument modifies the quality of the images, ranging from 0-100
/// where 100 is the original/max quality. If `imageQuality` is null, the images with
/// the original quality will be returned. Compression is only supported for certain
/// image types such as JPEG. If compression is not supported for the image that is picked,
/// a warning message will be logged.
///
/// If no images were picked, the return value is null.
Future<List<PickedFile>?> pickMultiImage({
double? maxWidth,
double? maxHeight,
int? imageQuality,
}) {
throw UnimplementedError('pickMultiImage() has not been implemented.');
}

/// Returns a [PickedFile] containing the video that was picked.
///
/// The [source] argument controls where the video comes from. This can
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: A common platform interface for the image_picker plugin.
homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker_platform_interface
# NOTE: We strongly prefer non-breaking changes, even at the expense of a
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
version: 2.0.1
version: 2.1.0

dependencies:
flutter:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ void main() {
MethodChannelImagePicker picker = MethodChannelImagePicker();

final List<MethodCall> log = <MethodCall>[];
dynamic returnValue = '';

setUp(() {
returnValue = '';
picker.channel.setMockMethodCallHandler((MethodCall methodCall) async {
log.add(methodCall);
return '';
return returnValue;
});

log.clear();
Expand Down Expand Up @@ -139,6 +141,29 @@ void main() {
);
});

test('does not accept a invalid imageQuality argument', () {
expect(
() => picker.pickImage(imageQuality: -1, source: ImageSource.gallery),
throwsArgumentError,
);

expect(
() =>
picker.pickImage(imageQuality: 101, source: ImageSource.gallery),
throwsArgumentError,
);

expect(
() => picker.pickImage(imageQuality: -1, source: ImageSource.camera),
throwsArgumentError,
);

expect(
() => picker.pickImage(imageQuality: 101, source: ImageSource.camera),
throwsArgumentError,
);
});

test('does not accept a negative width or height argument', () {
expect(
() => picker.pickImage(source: ImageSource.camera, maxWidth: -1.0),
Expand Down Expand Up @@ -196,6 +221,127 @@ void main() {
});
});

group('#pickMultiImage', () {
test('calls the method correctly', () async {
returnValue = ['0', '1'];
await picker.pickMultiImage();

expect(
log,
<Matcher>[
isMethodCall('pickMultiImage', arguments: <String, dynamic>{
'maxWidth': null,
'maxHeight': null,
'imageQuality': null,
}),
],
);
});

test('passes the width and height arguments correctly', () async {
returnValue = ['0', '1'];
await picker.pickMultiImage();
await picker.pickMultiImage(
maxWidth: 10.0,
);
await picker.pickMultiImage(
maxHeight: 10.0,
);
await picker.pickMultiImage(
maxWidth: 10.0,
maxHeight: 20.0,
);
await picker.pickMultiImage(
maxWidth: 10.0,
imageQuality: 70,
);
await picker.pickMultiImage(
maxHeight: 10.0,
imageQuality: 70,
);
await picker.pickMultiImage(
maxWidth: 10.0,
maxHeight: 20.0,
imageQuality: 70,
);

expect(
log,
<Matcher>[
isMethodCall('pickMultiImage', arguments: <String, dynamic>{
'maxWidth': null,
'maxHeight': null,
'imageQuality': null,
}),
isMethodCall('pickMultiImage', arguments: <String, dynamic>{
'maxWidth': 10.0,
'maxHeight': null,
'imageQuality': null,
}),
isMethodCall('pickMultiImage', arguments: <String, dynamic>{
'maxWidth': null,
'maxHeight': 10.0,
'imageQuality': null,
}),
isMethodCall('pickMultiImage', arguments: <String, dynamic>{
'maxWidth': 10.0,
'maxHeight': 20.0,
'imageQuality': null,
}),
isMethodCall('pickMultiImage', arguments: <String, dynamic>{
'maxWidth': 10.0,
'maxHeight': null,
'imageQuality': 70,
}),
isMethodCall('pickMultiImage', arguments: <String, dynamic>{
'maxWidth': null,
'maxHeight': 10.0,
'imageQuality': 70,
}),
isMethodCall('pickMultiImage', arguments: <String, dynamic>{
'maxWidth': 10.0,
'maxHeight': 20.0,
'imageQuality': 70,
}),
],
);
});

test('does not accept a negative width or height argument', () {
returnValue = ['0', '1'];
expect(
() => picker.pickMultiImage(maxWidth: -1.0),
throwsArgumentError,
);

expect(
() => picker.pickMultiImage(maxHeight: -1.0),
throwsArgumentError,
);
});

test('does not accept a invalid imageQuality argument', () {
returnValue = ['0', '1'];
expect(
() => picker.pickMultiImage(imageQuality: -1),
throwsArgumentError,
);

expect(
() => picker.pickMultiImage(imageQuality: 101),
throwsArgumentError,
);
});

test('handles a null image path response gracefully', () async {
picker.channel
.setMockMethodCallHandler((MethodCall methodCall) => null);

expect(await picker.pickMultiImage(), isNull);
expect(await picker.pickMultiImage(), isNull);
});
});

group('#pickVideoPath', () {
test('passes the image source argument correctly', () async {
await picker.pickVideo(source: ImageSource.camera);
Expand Down