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

[image_picker_platform_interface] Migrate image_picker to package:cross_file #4072

Merged
merged 9 commits into from
Jul 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 2.2.0

* Added new methods that return `XFile` (from `package:cross_file`)
* `getImage` (will deprecate `pickImage`)
* `getVideo` (will deprecate `pickVideo`)
* `getMultiImage` (will deprecate `pickMultiImage`)

_`PickedFile` will also be marked as deprecated in an upcoming release._

## 2.1.0

* Add `pickMultiImage` method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

export 'package:image_picker_platform_interface/src/platform_interface/image_picker_platform.dart';
export 'package:image_picker_platform_interface/src/types/types.dart';
export 'package:cross_file/cross_file.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
}) async {
String? path = await _pickImagePath(
String? path = await _getImagePath(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
Expand All @@ -42,21 +42,17 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
double? maxHeight,
int? imageQuality,
}) async {
final List<dynamic>? paths = await _pickMultiImagePath(
final List<dynamic>? paths = await _getMultiImagePath(
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;
return paths.map((path) => PickedFile(path)).toList();
}

Future<List<dynamic>?> _pickMultiImagePath({
Future<List<dynamic>?> _getMultiImagePath({
double? maxWidth,
double? maxHeight,
int? imageQuality,
Expand Down Expand Up @@ -84,7 +80,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
);
}

Future<String?> _pickImagePath({
Future<String?> _getImagePath({
required ImageSource source,
double? maxWidth,
double? maxHeight,
Expand Down Expand Up @@ -122,15 +118,15 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
}) async {
final String? path = await _pickVideoPath(
final String? path = await _getVideoPath(
source: source,
maxDuration: maxDuration,
preferredCameraDevice: preferredCameraDevice,
);
return path != null ? PickedFile(path) : null;
}

Future<String?> _pickVideoPath({
Future<String?> _getVideoPath({
required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
Expand All @@ -154,7 +150,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
return LostData.empty();
}

assert(result.containsKey('path') ^ result.containsKey('errorCode'));
assert(result.containsKey('path') != result.containsKey('errorCode'));

final String? type = result['type'];
assert(type == kTypeImage || type == kTypeVideo);
Expand All @@ -180,4 +176,88 @@ class MethodChannelImagePicker extends ImagePickerPlatform {
type: retrieveType,
);
}

@override
Future<XFile?> getImage({
required ImageSource source,
double? maxWidth,
double? maxHeight,
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
}) async {
String? path = await _getImagePath(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
preferredCameraDevice: preferredCameraDevice,
);
return path != null ? XFile(path) : null;
}

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

return paths.map((path) => XFile(path)).toList();
}

@override
Future<XFile?> getVideo({
required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
}) async {
final String? path = await _getVideoPath(
source: source,
maxDuration: maxDuration,
preferredCameraDevice: preferredCameraDevice,
);
return path != null ? XFile(path) : null;
}

@override
Future<LostDataResponse> getLostData() async {
final Map<String, dynamic>? result =
await _channel.invokeMapMethod<String, dynamic>('retrieve');

if (result == null) {
return LostDataResponse.empty();
}

assert(result.containsKey('path') != result.containsKey('errorCode'));

final String? type = result['type'];
assert(type == kTypeImage || type == kTypeVideo);

RetrieveType? retrieveType;
if (type == kTypeImage) {
retrieveType = RetrieveType.image;
} else if (type == kTypeVideo) {
retrieveType = RetrieveType.video;
}

PlatformException? exception;
if (result.containsKey('errorCode')) {
exception = PlatformException(
code: result['errorCode'], message: result['errorMessage']);
}

final String? path = result['path'];

return LostDataResponse(
file: path != null ? XFile(path) : null,
exception: exception,
type: retrieveType,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

import 'dart:async';

import 'package:cross_file/cross_file.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';

import 'package:image_picker_platform_interface/src/method_channel/method_channel_image_picker.dart';
import 'package:image_picker_platform_interface/src/types/types.dart';

Expand Down Expand Up @@ -144,4 +144,111 @@ abstract class ImagePickerPlatform extends PlatformInterface {
Future<LostData> retrieveLostData() {
throw UnimplementedError('retrieveLostData() has not been implemented.');
}

/// Returns an [XFile] with the image that was picked.
///
/// The `source` argument controls where the image comes from. This can
/// be either [ImageSource.camera] or [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 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 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.
///
/// 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.
/// Defaults to [CameraDevice.rear]. Note that Android has no documented parameter for an intent to specify if
/// the front or rear camera should be opened, this function is not guaranteed
/// to work on an Android device.
///
/// In Android, the MainActivity can be destroyed for various reasons. If that happens, the result will be lost
/// in this call. You can then call [getLostData] when your app relaunches to retrieve the lost data.
///
/// If no images were picked, the return value is null.
Future<XFile?> getImage({
required ImageSource source,
double? maxWidth,
double? maxHeight,
int? imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
}) {
throw UnimplementedError('getImage() has not been implemented.');
}

/// Returns a [List<XFile>] 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<XFile>?> getMultiImage({
double? maxWidth,
double? maxHeight,
int? imageQuality,
}) {
throw UnimplementedError('getMultiImage() has not been implemented.');
}

/// Returns a [XFile] containing the video that was picked.
///
/// The [source] argument controls where the video comes from. This can
/// be either [ImageSource.camera] or [ImageSource.gallery].
///
/// The [maxDuration] argument specifies the maximum duration of the captured video. If no [maxDuration] is specified,
/// the maximum duration will be infinite.
///
/// 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.
/// Defaults to [CameraDevice.rear].
///
/// In Android, the MainActivity can be destroyed for various fo reasons. If that happens, the result will be lost
/// in this call. You can then call [getLostData] when your app relaunches to retrieve the lost data.
///
/// If no images were picked, the return value is null.
Future<XFile?> getVideo({
required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration? maxDuration,
}) {
throw UnimplementedError('getVideo() has not been implemented.');
}

/// Retrieve the lost [XFile] file when [getImage], [getMultiImage] or [getVideo] failed because the MainActivity is
/// destroyed. (Android only)
///
/// Image or video can be lost if the MainActivity is destroyed. And there is no guarantee that the MainActivity is
/// always alive. Call this method to retrieve the lost data and process the data according to your APP's business logic.
///
/// Returns a [LostDataResponse] object if successfully retrieved the lost data. The [LostDataResponse] object can
/// represent either a successful image/video selection, or a failure.
///
/// Calling this on a non-Android platform will throw [UnimplementedError] exception.
///
/// See also:
/// * [LostDataResponse], for what's included in the response.
/// * [Android Activity Lifecycle](https://developer.android.com/reference/android/app/Activity.html), for more
/// information on MainActivity destruction.
Future<LostDataResponse> getLostData() {
throw UnimplementedError('getLostData() has not been implemented.');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:cross_file/cross_file.dart';
import 'package:flutter/services.dart';
import 'package:image_picker_platform_interface/src/types/types.dart';

/// The response object of [ImagePicker.getLostData].
///
/// Only applies to Android.
/// See also:
/// * [ImagePicker.getLostData] for more details on retrieving lost data.
class LostDataResponse {
/// Creates an instance with the given [file], [exception], and [type]. Any of
/// the params may be null, but this is never considered to be empty.
LostDataResponse({this.file, this.exception, this.type});

/// Initializes an instance with all member params set to null and considered
/// to be empty.
LostDataResponse.empty()
: file = null,
exception = null,
type = null,
_empty = true;

/// Whether it is an empty response.
///
/// An empty response should have [file], [exception] and [type] to be null.
bool get isEmpty => _empty;

/// The file that was lost in a previous [getImage], [getMultiImage] or [getVideo] call due to MainActivity being destroyed.
///
/// Can be null if [exception] exists.
final XFile? file;

/// The exception of the last [getImage], [getMultiImage] or [getVideo].
///
/// If the last [getImage], [getMultiImage] or [getVideo] threw some exception before the MainActivity destruction,
/// this variable keeps that exception.
/// You should handle this exception as if the [getImage], [getMultiImage] or [getVideo] got an exception when
/// the MainActivity was not destroyed.
///
/// Note that it is not the exception that caused the destruction of the MainActivity.
final PlatformException? exception;

/// Can either be [RetrieveType.image] or [RetrieveType.video];
///
/// If the lost data is empty, this will be null.
final RetrieveType? type;

bool _empty = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export 'camera_device.dart';
export 'image_source.dart';
export 'retrieve_type.dart';
export 'picked_file/picked_file.dart';
export 'lost_data_response.dart';

/// Denotes that an image is being picked.
const String kTypeImage = 'image';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/image_picker
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22
# 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.1.0
version: 2.2.0

environment:
sdk: ">=2.12.0 <3.0.0"
Expand All @@ -16,6 +16,7 @@ dependencies:
http: ^0.13.0
meta: ^1.3.0
plugin_platform_interface: ^2.0.0
cross_file: ^0.3.1+1

dev_dependencies:
flutter_test:
Expand Down
Loading