| // 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 'dart:async'; |
| |
| import 'package:cross_file/cross_file.dart'; |
| import 'package:plugin_platform_interface/plugin_platform_interface.dart'; |
| |
| import '../method_channel/method_channel_image_picker.dart'; |
| import '../types/types.dart'; |
| |
| /// The interface that implementations of image_picker must implement. |
| /// |
| /// Platform implementations should extend this class rather than implement it as `image_picker` |
| /// does not consider newly added methods to be breaking changes. Extending this class |
| /// (using `extends`) ensures that the subclass will get the default implementation, while |
| /// platform implementations that `implements` this interface will be broken by newly added |
| /// [ImagePickerPlatform] methods. |
| abstract class ImagePickerPlatform extends PlatformInterface { |
| /// Constructs a ImagePickerPlatform. |
| ImagePickerPlatform() : super(token: _token); |
| |
| static final Object _token = Object(); |
| |
| static ImagePickerPlatform _instance = MethodChannelImagePicker(); |
| |
| /// The default instance of [ImagePickerPlatform] to use. |
| /// |
| /// Defaults to [MethodChannelImagePicker]. |
| static ImagePickerPlatform get instance => _instance; |
| |
| /// Platform-specific plugins should set this with their own platform-specific |
| /// class that extends [ImagePickerPlatform] when they register themselves. |
| static set instance(ImagePickerPlatform instance) { |
| PlatformInterface.verify(instance, _token); |
| _instance = instance; |
| } |
| |
| /// Returns a [PickedFile] 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 [retrieveLostData] when your app relaunches to retrieve the lost data. |
| /// |
| /// If no images were picked, the return value is null. |
| @Deprecated('Use getImageFromSource instead.') |
| Future<PickedFile?> pickImage({ |
| required ImageSource source, |
| double? maxWidth, |
| double? maxHeight, |
| int? imageQuality, |
| CameraDevice preferredCameraDevice = CameraDevice.rear, |
| }) { |
| 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. |
| @Deprecated('Use getMultiImageWithOptions instead.') |
| 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 |
| /// 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 reasons. If that happens, the result will be lost |
| /// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data. |
| /// |
| /// If no images were picked, the return value is null. |
| @Deprecated('Use getVideo instead.') |
| Future<PickedFile?> pickVideo({ |
| required ImageSource source, |
| CameraDevice preferredCameraDevice = CameraDevice.rear, |
| Duration? maxDuration, |
| }) { |
| throw UnimplementedError('pickVideo() has not been implemented.'); |
| } |
| |
| /// Retrieves any previously picked file, that was lost due to the MainActivity being destroyed. |
| /// In case multiple files were lost, only the last file will be recovered. (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 [LostData] object if successfully retrieved the lost data. The [LostData] 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: |
| /// * [LostData], 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. |
| @Deprecated('Use getLostData instead.') |
| 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. |
| @Deprecated('Use getImageFromSource instead.') |
| 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. |
| @Deprecated('Use getMultiImageWithOptions instead.') |
| Future<List<XFile>?> getMultiImage({ |
| double? maxWidth, |
| double? maxHeight, |
| int? imageQuality, |
| }) { |
| throw UnimplementedError('getMultiImage() has not been implemented.'); |
| } |
| |
| /// Returns a [List<XFile>] with the images and/or videos that were picked. |
| /// The images and videos come from the 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. |
| /// |
| /// 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 or videos were picked, the return value is an empty list. |
| Future<List<XFile>> getMedia({ |
| required MediaOptions options, |
| }) { |
| throw UnimplementedError('getMedia() 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 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.'); |
| } |
| |
| /// Retrieves any previously picked files, that were lost due to the MainActivity being 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.'); |
| } |
| |
| /// 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]. |
| /// |
| /// The `options` argument controls additional settings that can be used when |
| /// picking an image. See [ImagePickerOptions] for more details. |
| /// |
| /// 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 in [ImagePickerOptions]. |
| /// |
| /// 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?> getImageFromSource({ |
| required ImageSource source, |
| ImagePickerOptions options = const ImagePickerOptions(), |
| }) { |
| return getImage( |
| source: source, |
| maxHeight: options.maxHeight, |
| maxWidth: options.maxWidth, |
| imageQuality: options.imageQuality, |
| preferredCameraDevice: options.preferredCameraDevice, |
| ); |
| } |
| |
| /// Returns a [List<XFile>] with the images that were picked. |
| /// |
| /// The images come from the [ImageSource.gallery]. |
| /// |
| /// The `options` argument controls additional settings that can be used when |
| /// picking an image. See [MultiImagePickerOptions] for more details. |
| /// |
| /// If no images were picked, returns an empty list. |
| Future<List<XFile>> getMultiImageWithOptions({ |
| MultiImagePickerOptions options = const MultiImagePickerOptions(), |
| }) async { |
| final List<XFile>? pickedImages = await getMultiImage( |
| maxWidth: options.imageOptions.maxWidth, |
| maxHeight: options.imageOptions.maxHeight, |
| imageQuality: options.imageOptions.imageQuality, |
| ); |
| return pickedImages ?? <XFile>[]; |
| } |
| |
| /// Returns true if the implementation supports [source]. |
| /// |
| /// Defaults to true for the original image sources, `gallery` and `camera`, |
| /// for backwards compatibility. |
| bool supportsImageSource(ImageSource source) { |
| return source == ImageSource.gallery || source == ImageSource.camera; |
| } |
| } |
| |
| /// A base class for an [ImagePickerPlatform] implementation that does not |
| /// directly support [ImageSource.camera], but supports delegating to a |
| /// provided [ImagePickerCameraDelegate]. |
| abstract class CameraDelegatingImagePickerPlatform extends ImagePickerPlatform { |
| /// A delegate to respond to calls that use [ImageSource.camera]. |
| /// |
| /// When it is null, attempting to use [ImageSource.camera] will throw a |
| /// [StateError]. |
| ImagePickerCameraDelegate? cameraDelegate; |
| |
| @override |
| bool supportsImageSource(ImageSource source) { |
| if (source == ImageSource.camera) { |
| return cameraDelegate != null; |
| } |
| return super.supportsImageSource(source); |
| } |
| |
| @override |
| Future<XFile?> getImageFromSource({ |
| required ImageSource source, |
| ImagePickerOptions options = const ImagePickerOptions(), |
| }) async { |
| if (source == ImageSource.camera) { |
| final ImagePickerCameraDelegate? delegate = cameraDelegate; |
| if (delegate == null) { |
| throw StateError( |
| 'This implementation of ImagePickerPlatform requires a ' |
| '"cameraDelegate" in order to use ImageSource.camera'); |
| } |
| return delegate.takePhoto( |
| options: ImagePickerCameraDelegateOptions( |
| preferredCameraDevice: options.preferredCameraDevice, |
| )); |
| } |
| return super.getImageFromSource(source: source, options: options); |
| } |
| |
| @override |
| Future<XFile?> getVideo({ |
| required ImageSource source, |
| CameraDevice preferredCameraDevice = CameraDevice.rear, |
| Duration? maxDuration, |
| }) async { |
| if (source == ImageSource.camera) { |
| final ImagePickerCameraDelegate? delegate = cameraDelegate; |
| if (delegate == null) { |
| throw StateError( |
| 'This implementation of ImagePickerPlatform requires a ' |
| '"cameraDelegate" in order to use ImageSource.camera'); |
| } |
| return delegate.takeVideo( |
| options: ImagePickerCameraDelegateOptions( |
| preferredCameraDevice: preferredCameraDevice, |
| maxVideoDuration: maxDuration)); |
| } |
| return super.getVideo( |
| source: source, |
| preferredCameraDevice: preferredCameraDevice, |
| maxDuration: maxDuration); |
| } |
| } |