From fcb1fe48e19ba40e0ddeb23cb7708a30cbf4209b Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 14 May 2020 17:16:54 -0700 Subject: [PATCH 1/7] Introduce the PickedFile class to have a more platform agnostic return. Borrowed from Rody Davis' file_access package. Co-authored-by: Rody Davis --- .../lib/src/types/picked_file/base.dart | 56 +++++++++++++++++++ .../lib/src/types/picked_file/html.dart | 33 +++++++++++ .../lib/src/types/picked_file/io.dart | 35 ++++++++++++ .../lib/src/types/picked_file/lost_data.dart | 49 ++++++++++++++++ .../src/types/picked_file/picked_file.dart | 4 ++ .../src/types/picked_file/unsupported.dart | 14 +++++ 6 files changed, 191 insertions(+) create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart new file mode 100644 index 000000000000..01900fb71da6 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart @@ -0,0 +1,56 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:meta/meta.dart'; + +/// The interface for a PickedFile. +/// +/// A PickedFile is a container that wraps the path of a selected +/// file by the user and (in some platforms, like web) the bytes +/// with the contents of the file. +/// +/// This class is a very limited subset of dart:io [File], so all +/// the methods should seem familiar. +@immutable +abstract class PickedFileBase { + /// Construct a PickedFile + PickedFileBase(String path); + + /// Get the path of the picked file. + /// + /// This should only be used as a backwards-compatibility clutch + /// for mobile apps, or cosmetic reasons only (to show the user + /// the path they've picked). + /// + /// Accessing the data contained in the picked file by its path + /// is platform-dependant (and won't work on web), so use the + /// byte getters in the PickedFile instance instead. + String get path { + throw UnimplementedError('.path has not been implemented.'); + } + + /// Synchronously read the entire file contents as a string using the given [Encoding]. + /// + /// Throws Exception if the operation fails. + String readAsStringSync({Encoding encoding = utf8}) { + throw UnimplementedError('readAsStringSync() has not been implemented.'); + } + + /// Synchronously read the entire file contents as a list of bytes. + /// + /// Throws Exception if the operation fails. + Uint8List readAsBytesSync() { + throw UnimplementedError('readAsBytesSync() has not been implemented.'); + } + + /// Create a new independent [Stream] for the contents of this file. + /// + /// If `start` is present, the file will be read from byte-offset `start`. Otherwise from the beginning (index 0). + /// + /// If `end` is present, only up to byte-index `end` will be read. Otherwise, until end of file. + /// + /// In order to make sure that system resources are freed, the stream must be read to completion or the subscription on the stream must be cancelled. + Stream openRead([int start, int end]) { + throw UnimplementedError('openRead() has not been implemented.'); + } +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart new file mode 100644 index 000000000000..7b211c6bdff8 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart @@ -0,0 +1,33 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import './base.dart'; + +/// A PickedFile that works on web. +/// +/// It wraps the bytes of a selected file. +class PickedFile extends PickedFileBase { + final String path; + final Uint8List _bytes; + + /// Construct a PickedFile object, from its `bytes`. + PickedFile(this.path, {Uint8List bytes}) + : _bytes = bytes, + super(path); + + @override + String readAsStringSync({Encoding encoding = utf8}) { + return encoding.decode(_bytes); + } + + @override + Uint8List readAsBytesSync() { + return UnmodifiableUint8ListView(_bytes); + } + + @override + Stream openRead([int start, int end]) { + return Stream.fromIterable( + [_bytes.sublist(start ?? 0, end ?? _bytes.length)]); + } +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart new file mode 100644 index 000000000000..7c229913c3e5 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart @@ -0,0 +1,35 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import './base.dart'; + +/// A PickedFile backed by a dart:io File. +class PickedFile extends PickedFileBase { + final File _file; + + /// Construct a PickedFile object backed by a dart:io File. + PickedFile(String path) + : _file = File(path), + super(path); + + @override + String get path { + return _file.path; + } + + @override + String readAsStringSync({Encoding encoding = utf8}) { + return _file.readAsStringSync(encoding: encoding); + } + + @override + Uint8List readAsBytesSync() { + return _file.readAsBytesSync(); + } + + @override + Stream openRead([int start, int end]) { + return _file.openRead(start ?? 0, end); + } +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart new file mode 100644 index 000000000000..b94e69de219e --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart @@ -0,0 +1,49 @@ +// Copyright 2017 The Chromium 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:flutter/services.dart'; +import 'package:image_picker_platform_interface/src/types/types.dart'; + +/// The response object of [ImagePicker.retrieveLostData]. +/// +/// Only applies to Android. +/// See also: +/// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. +class LostData { + /// 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. + LostData({this.file, this.exception, this.type}); + + /// Initializes an instance with all member params set to null and considered + /// to be empty. + LostData.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 [pickImage] or [pickVideo] call due to MainActivity being destroyed. + /// + /// Can be null if [exception] exists. + final PickedFile file; + + /// The exception of the last [pickImage] or [pickVideo]. + /// + /// If the last [pickImage] or [pickVideo] threw some exception before the MainActivity destruction, this variable keeps that + /// exception. + /// You should handle this exception as if the [pickImage] or [pickVideo] 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]; + final RetrieveType type; + + bool _empty = false; +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart new file mode 100644 index 000000000000..b2a614ccb304 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/picked_file.dart @@ -0,0 +1,4 @@ +export 'lost_data.dart'; +export 'unsupported.dart' + if (dart.library.html) 'html.dart' + if (dart.library.io) 'io.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart new file mode 100644 index 000000000000..bc10a4890c8d --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/unsupported.dart @@ -0,0 +1,14 @@ +import './base.dart'; + +/// A PickedFile is a cross-platform, simplified File abstraction. +/// +/// It wraps the bytes of a selected file, and its (platform-dependant) path. +class PickedFile extends PickedFileBase { + /// Construct a PickedFile object, from its `bytes`. + /// + /// Optionally, you may pass a `path`. See caveats in [PickedFileBase.path]. + PickedFile(String path) : super(path) { + throw UnimplementedError( + 'PickedFile is not available in your current platform.'); + } +} From f25265f1b0a29b276a4f3c21df47e29a7e8b7639 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Thu, 14 May 2020 17:21:04 -0700 Subject: [PATCH 2/7] Modify the platform interface to return PickedFiles. --- .../CHANGELOG.md | 4 + .../method_channel_image_picker.dart | 68 ++++++++++++++++ .../image_picker_platform.dart | 77 ++++++++++++++++++- .../lib/src/types/lost_data_response.dart | 1 + .../lib/src/types/types.dart | 1 + .../pubspec.yaml | 4 +- 6 files changed, 152 insertions(+), 3 deletions(-) diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 7708c34ffe8c..0a238bcd51bf 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.1.0 + +* Introduce PickedFile type for the new API. + ## 1.0.1 * Update lower bound of dart dependency to 2.1.0. diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index 4d960517b73b..a9dadbf68df4 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -19,6 +19,24 @@ class MethodChannelImagePicker extends ImagePickerPlatform { @visibleForTesting MethodChannel get channel => _channel; + @override + Future pickImage({ + @required ImageSource source, + double maxWidth, + double maxHeight, + int imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + String path = await pickImagePath( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? PickedFile(path) : null; + } + @override Future pickImagePath({ @required ImageSource source, @@ -53,6 +71,20 @@ class MethodChannelImagePicker extends ImagePickerPlatform { ); } + @override + Future pickVideo({ + @required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration maxDuration, + }) async { + String path = await pickVideoPath( + source: source, + maxDuration: maxDuration, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? PickedFile(path) : null; + } + @override Future pickVideoPath({ @required ImageSource source, @@ -70,6 +102,42 @@ class MethodChannelImagePicker extends ImagePickerPlatform { ); } + @override + Future retrieveLostData() async { + final Map result = + await _channel.invokeMapMethod('retrieve'); + + if (result == null) { + return LostData.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 LostData( + file: path != null ? PickedFile(path) : null, + exception: exception, + type: retrieveType, + ); + } + @override Future retrieveLostDataAsDartIoFile() async { final Map result = diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index 66e74dd95636..94be4c2f2ab1 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -60,6 +60,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// /// 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 [retrieveLostDataAsDartIoFile] when your app relaunches to retrieve the lost data. + @Deprecated('Use pickImage instead.') Future pickImagePath({ @required ImageSource source, double maxWidth, @@ -84,6 +85,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// /// 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 [retrieveLostDataAsDartIoFile] when your app relaunches to retrieve the lost data. + @Deprecated('Use pickVideo instead.') Future pickVideoPath({ @required ImageSource source, CameraDevice preferredCameraDevice = CameraDevice.rear, @@ -92,7 +94,7 @@ abstract class ImagePickerPlatform extends PlatformInterface { throw UnimplementedError('pickVideoPath() has not been implemented.'); } - /// Retrieve the lost image file when [pickImage] or [pickVideo] failed because the MainActivity is destroyed. (Android only) + /// Retrieve the lost image file when [pickImagePath] or [pickVideoPath] 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. @@ -105,8 +107,81 @@ abstract class ImagePickerPlatform extends PlatformInterface { /// 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. + @Deprecated('Use retrieveLostData instead.') Future retrieveLostDataAsDartIoFile() { throw UnimplementedError( 'retrieveLostDataAsDartIoFile() has not been implemented.'); } + + // Next version of the API. + + /// 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]. + /// + /// 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 supportted for certain + /// image types such as JPEG. If compression is not supported for the image that is picked, + /// an 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]. + /// + /// 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. + Future pickImage({ + @required ImageSource source, + double maxWidth, + double maxHeight, + int imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) { + throw UnimplementedError('pickImage() 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 fo 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. + Future pickVideo({ + @required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration maxDuration, + }) { + throw UnimplementedError('pickVideo() has not been implemented.'); + } + + /// Retrieve the lost [PickedFile] file when [pickImage] or [pickVideo] 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 [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. + Future retrieveLostData() { + throw UnimplementedError('retrieveLostData() has not been implemented.'); + } } diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart index 53e2decd123f..d82618b23cd1 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart @@ -12,6 +12,7 @@ import 'package:image_picker_platform_interface/src/types/types.dart'; /// Only applies to Android. /// See also: /// * [ImagePicker.retrieveLostData] for more details on retrieving lost data. +@Deprecated('Use methods that return a LostData object instead.') 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. diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart index 98418109dc09..9c44fae1aa9d 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart @@ -2,6 +2,7 @@ export 'camera_device.dart'; export 'image_source.dart'; export 'lost_data_response.dart'; export 'retrieve_type.dart'; +export 'picked_file/picked_file.dart'; /// Denotes that an image is being picked. const String kTypeImage = 'image'; diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index a4ea5d1f959a..e4e4a7ad1c8e 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -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: 1.0.1 +version: 1.1.0 dependencies: flutter: @@ -18,5 +18,5 @@ dev_dependencies: pedantic: ^1.8.0+1 environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.5.0 <3.0.0" flutter: ">=1.10.0 <2.0.0" From fa8cf32c75bfc67ae16db3b5808e7e73e457d837 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Fri, 15 May 2020 15:29:10 -0700 Subject: [PATCH 3/7] Make API Async, so web can use objectUrls internally, instead of bytes. --- .../lib/src/types/picked_file/base.dart | 8 ++--- .../lib/src/types/picked_file/html.dart | 32 +++++++++++++------ .../lib/src/types/picked_file/io.dart | 8 ++--- .../pubspec.yaml | 1 + 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart index 01900fb71da6..08afe6f1e41e 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart @@ -32,15 +32,15 @@ abstract class PickedFileBase { /// Synchronously read the entire file contents as a string using the given [Encoding]. /// /// Throws Exception if the operation fails. - String readAsStringSync({Encoding encoding = utf8}) { - throw UnimplementedError('readAsStringSync() has not been implemented.'); + Future readAsString({Encoding encoding = utf8}) { + throw UnimplementedError('readAsString() has not been implemented.'); } /// Synchronously read the entire file contents as a list of bytes. /// /// Throws Exception if the operation fails. - Uint8List readAsBytesSync() { - throw UnimplementedError('readAsBytesSync() has not been implemented.'); + Future readAsBytes() { + throw UnimplementedError('readAsBytes() has not been implemented.'); } /// Create a new independent [Stream] for the contents of this file. diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart index 7b211c6bdff8..0faf531f3f75 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/html.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:http/http.dart' as http show readBytes; + import './base.dart'; /// A PickedFile that works on web. @@ -8,26 +10,36 @@ import './base.dart'; /// It wraps the bytes of a selected file. class PickedFile extends PickedFileBase { final String path; - final Uint8List _bytes; + final Uint8List _initBytes; - /// Construct a PickedFile object, from its `bytes`. + /// Construct a PickedFile object from its ObjectUrl. + /// + /// Optionally, this can be initialized with `bytes` + /// so no http requests are performed to retrieve files later. PickedFile(this.path, {Uint8List bytes}) - : _bytes = bytes, + : _initBytes = bytes, super(path); + Future get _bytes async { + if (_initBytes != null) { + return Future.value(UnmodifiableUint8ListView(_initBytes)); + } + return http.readBytes(path); + } + @override - String readAsStringSync({Encoding encoding = utf8}) { - return encoding.decode(_bytes); + Future readAsString({Encoding encoding = utf8}) async { + return encoding.decode(await _bytes); } @override - Uint8List readAsBytesSync() { - return UnmodifiableUint8ListView(_bytes); + Future readAsBytes() async { + return Future.value(await _bytes); } @override - Stream openRead([int start, int end]) { - return Stream.fromIterable( - [_bytes.sublist(start ?? 0, end ?? _bytes.length)]); + Stream openRead([int start, int end]) async* { + final bytes = await _bytes; + yield bytes.sublist(start ?? 0, end ?? bytes.length); } } diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart index 7c229913c3e5..6fbde4943290 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart @@ -19,13 +19,13 @@ class PickedFile extends PickedFileBase { } @override - String readAsStringSync({Encoding encoding = utf8}) { - return _file.readAsStringSync(encoding: encoding); + Future readAsString({Encoding encoding = utf8}) { + return _file.readAsString(encoding: encoding); } @override - Uint8List readAsBytesSync() { - return _file.readAsBytesSync(); + Future readAsBytes() { + return _file.readAsBytes(); } @override diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index e4e4a7ad1c8e..946cf80c5187 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: flutter: sdk: flutter meta: ^1.1.8 + http: ^0.12.1 plugin_platform_interface: ^1.0.2 dev_dependencies: From a36c6ac30c2ecf9a762da1fe83670f5fad4388e3 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Mon, 18 May 2020 14:15:54 -0700 Subject: [PATCH 4/7] [image_picker_platform_interface] Ignore deprecated use analyzer warnings --- .../lib/src/method_channel/method_channel_image_picker.dart | 3 +++ .../test/method_channel_image_picker_test.dart | 2 ++ 2 files changed, 5 insertions(+) diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index a9dadbf68df4..71704b63ced4 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -139,10 +139,12 @@ class MethodChannelImagePicker extends ImagePickerPlatform { } @override + // ignore: deprecated_member_use_from_same_package Future retrieveLostDataAsDartIoFile() async { final Map result = await _channel.invokeMapMethod('retrieve'); if (result == null) { + // ignore: deprecated_member_use_from_same_package return LostDataResponse.empty(); } assert(result.containsKey('path') ^ result.containsKey('errorCode')); @@ -165,6 +167,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { final String path = result['path']; + // ignore: deprecated_member_use_from_same_package return LostDataResponse( file: path == null ? null : File(path), exception: exception, diff --git a/packages/image_picker/image_picker_platform_interface/test/method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/method_channel_image_picker_test.dart index 701379b84aae..e6968b90f36c 100644 --- a/packages/image_picker/image_picker_platform_interface/test/method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/method_channel_image_picker_test.dart @@ -299,6 +299,7 @@ void main() { 'path': '/example/path', }; }); + // ignore: deprecated_member_use_from_same_package final LostDataResponse response = await picker.retrieveLostDataAsDartIoFile(); expect(response.type, RetrieveType.image); @@ -313,6 +314,7 @@ void main() { 'errorMessage': 'test_error_message', }; }); + // ignore: deprecated_member_use_from_same_package final LostDataResponse response = await picker.retrieveLostDataAsDartIoFile(); expect(response.type, RetrieveType.video); From c31b6bf04bace69feaf34525a734df1e67c595d7 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 20 May 2020 16:52:39 -0700 Subject: [PATCH 5/7] [image_picker_platform_interface] Add unit tests Run with flutter test / flutter test --platform chrome --- .../lib/src/types/picked_file/io.dart | 2 +- .../test/assets/hello.txt | 1 + .../method_channel_image_picker_test.dart | 2 +- .../new_method_channel_image_picker_test.dart | 345 ++++++++++++++++++ .../test/picked_file_html_test.dart | 39 ++ .../test/picked_file_io_test.dart | 39 ++ 6 files changed, 426 insertions(+), 2 deletions(-) create mode 100644 packages/image_picker/image_picker_platform_interface/test/assets/hello.txt create mode 100644 packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart create mode 100644 packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart create mode 100644 packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart index 6fbde4943290..cd96f4ba89bc 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart @@ -30,6 +30,6 @@ class PickedFile extends PickedFileBase { @override Stream openRead([int start, int end]) { - return _file.openRead(start ?? 0, end); + return _file.openRead(start ?? 0, end).map((chunk) => Uint8List.fromList(chunk)); } } diff --git a/packages/image_picker/image_picker_platform_interface/test/assets/hello.txt b/packages/image_picker/image_picker_platform_interface/test/assets/hello.txt new file mode 100644 index 000000000000..5dd01c177f5d --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/test/assets/hello.txt @@ -0,0 +1 @@ +Hello, world! \ No newline at end of file diff --git a/packages/image_picker/image_picker_platform_interface/test/method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/method_channel_image_picker_test.dart index e6968b90f36c..ddaad3d32f41 100644 --- a/packages/image_picker/image_picker_platform_interface/test/method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/method_channel_image_picker_test.dart @@ -340,6 +340,6 @@ void main() { }); expect(picker.retrieveLostDataAsDartIoFile(), throwsAssertionError); }); - }); + }, skip: isBrowser); }); } diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart new file mode 100644 index 000000000000..714f9b4b8061 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -0,0 +1,345 @@ +// Copyright 2019 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:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:image_picker_platform_interface/src/method_channel/method_channel_image_picker.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$MethodChannelImagePicker', () { + MethodChannelImagePicker picker = MethodChannelImagePicker(); + + final List log = []; + + setUp(() { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return ''; + }); + + log.clear(); + }); + + group('#pickImage', () { + test('passes the image source argument correctly', () async { + await picker.pickImage(source: ImageSource.camera); + await picker.pickImage(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 1, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.pickImage(source: ImageSource.camera); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxHeight: 10.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.pickImage( + source: ImageSource.camera, maxWidth: 10.0, imageQuality: 70,); + await picker.pickImage( + source: ImageSource.camera, maxHeight: 10.0, imageQuality: 70,); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70,); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': 0 + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => + picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => + picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + picker.channel + .setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.pickImage(source: ImageSource.gallery), isNull); + expect(await picker.pickImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickImage(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 1, + }), + ], + ); + }); + }); + + group('#pickVideoPath', () { + test('passes the image source argument correctly', () async { + await picker.pickVideo(source: ImageSource.camera); + await picker.pickVideo(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + isMethodCall('pickVideo', arguments: { + 'source': 1, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('passes the duration argument correctly', () async { + await picker.pickVideo(source: ImageSource.camera); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10),); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1),); + await picker.pickVideo( + source: ImageSource.camera, maxDuration: const Duration(hours: 1),); + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 10, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 60, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 3600, + 'cameraDevice': 0, + }), + ], + ); + }); + + test('handles a null video path response gracefully', () async { + picker.channel + .setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.pickVideo(source: ImageSource.gallery), isNull); + expect(await picker.pickVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickVideo(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front,); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 1, + }), + ], + ); + }); + }); + + group('#retrieveLostData', () { + test('retrieveLostData get success response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'image', + 'path': '/example/path', + }; + }); + // ignore: deprecated_member_use_from_same_package + final LostData response = + await picker.retrieveLostData(); + expect(response.type, RetrieveType.image); + expect(response.file.path, '/example/path'); + }); + + test('retrieveLostData get error response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + }; + }); + // ignore: deprecated_member_use_from_same_package + final LostData response = + await picker.retrieveLostData(); + expect(response.type, RetrieveType.video); + expect(response.exception.code, 'test_error_code'); + expect(response.exception.message, 'test_error_message'); + }); + + test('retrieveLostData get null response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return null; + }); + expect((await picker.retrieveLostData()).isEmpty, true); + }); + + test('retrieveLostData get both path and error should throw', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + 'path': '/example/path', + }; + }); + expect(picker.retrieveLostData(), throwsAssertionError); + }); + }); + }); +} diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart new file mode 100644 index 000000000000..01af03b1abbc --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart @@ -0,0 +1,39 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@TestOn('chrome') // Uses web-only Flutter SDK + +import 'dart:convert'; +import 'dart:html' as html; +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +final String expectedStringContents = 'Hello, world!'; +final Uint8List bytes = utf8.encode(expectedStringContents); +final html.File textFile = html.File([bytes], 'hello.txt'); +final String textFileUrl = html.Url.createObjectUrl(textFile); + +void main() { + + group('Create with an objectUrl', () { + final pickedFile = PickedFile(textFileUrl); + + test('Can be read as a string', () async { + expect(await pickedFile.readAsString(), equals(expectedStringContents)); + }); + test('Can be read as bytes', () async { + expect(await pickedFile.readAsBytes(), equals(bytes)); + }); + + test('Can be read as a stream', () async { + expect(await pickedFile.openRead().first, equals(bytes)); + }); + + test('Stream can be sliced', () async { + expect(await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); + }); + }); +} diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart new file mode 100644 index 000000000000..aa9986cce91f --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart @@ -0,0 +1,39 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@TestOn('vm') // Uses dart:io + +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +final String expectedStringContents = 'Hello, world!'; +final Uint8List bytes = utf8.encode(expectedStringContents); +final File textFile = File('./assets/hello.txt'); +final String textFilePath = textFile.path; + +void main() { + + group('Create with an objectUrl', () { + final pickedFile = PickedFile(textFilePath); + + test('Can be read as a string', () async { + expect(await pickedFile.readAsString(), equals(expectedStringContents)); + }); + test('Can be read as bytes', () async { + expect(await pickedFile.readAsBytes(), equals(bytes)); + }); + + test('Can be read as a stream', () async { + expect(await pickedFile.openRead().first, equals(bytes)); + }); + + test('Stream can be sliced', () async { + expect(await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); + }); + }); +} From b1df591f2b317eeef415614a99621e012ca31ad5 Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Wed, 20 May 2020 16:58:53 -0700 Subject: [PATCH 6/7] [image_picker_platform_interface] dartfmt -w . --- .../lib/src/types/picked_file/io.dart | 4 +- .../new_method_channel_image_picker_test.dart | 50 +++++++++++-------- .../test/picked_file_html_test.dart | 4 +- .../test/picked_file_io_test.dart | 4 +- 4 files changed, 36 insertions(+), 26 deletions(-) diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart index cd96f4ba89bc..dd64558bf044 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/io.dart @@ -30,6 +30,8 @@ class PickedFile extends PickedFileBase { @override Stream openRead([int start, int end]) { - return _file.openRead(start ?? 0, end).map((chunk) => Uint8List.fromList(chunk)); + return _file + .openRead(start ?? 0, end) + .map((chunk) => Uint8List.fromList(chunk)); } } diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 714f9b4b8061..e7abe37e4838 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -67,14 +67,21 @@ void main() { maxHeight: 20.0, ); await picker.pickImage( - source: ImageSource.camera, maxWidth: 10.0, imageQuality: 70,); + source: ImageSource.camera, + maxWidth: 10.0, + imageQuality: 70, + ); await picker.pickImage( - source: ImageSource.camera, maxHeight: 10.0, imageQuality: 70,); + source: ImageSource.camera, + maxHeight: 10.0, + imageQuality: 70, + ); await picker.pickImage( - source: ImageSource.camera, - maxWidth: 10.0, - maxHeight: 20.0, - imageQuality: 70,); + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); expect( log, @@ -134,14 +141,12 @@ void main() { test('does not accept a negative width or height argument', () { expect( - () => - picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), + () => picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), throwsArgumentError, ); expect( - () => - picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), + () => picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), throwsArgumentError, ); }); @@ -216,13 +221,17 @@ void main() { test('passes the duration argument correctly', () async { await picker.pickVideo(source: ImageSource.camera); await picker.pickVideo( - source: ImageSource.camera, - maxDuration: const Duration(seconds: 10),); + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10), + ); await picker.pickVideo( - source: ImageSource.camera, - maxDuration: const Duration(minutes: 1),); + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); await picker.pickVideo( - source: ImageSource.camera, maxDuration: const Duration(hours: 1),); + source: ImageSource.camera, + maxDuration: const Duration(hours: 1), + ); expect( log, [ @@ -275,8 +284,9 @@ void main() { test('camera position can set to front', () async { await picker.pickVideo( - source: ImageSource.camera, - preferredCameraDevice: CameraDevice.front,); + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front, + ); expect( log, @@ -300,8 +310,7 @@ void main() { }; }); // ignore: deprecated_member_use_from_same_package - final LostData response = - await picker.retrieveLostData(); + final LostData response = await picker.retrieveLostData(); expect(response.type, RetrieveType.image); expect(response.file.path, '/example/path'); }); @@ -315,8 +324,7 @@ void main() { }; }); // ignore: deprecated_member_use_from_same_package - final LostData response = - await picker.retrieveLostData(); + final LostData response = await picker.retrieveLostData(); expect(response.type, RetrieveType.video); expect(response.exception.code, 'test_error_code'); expect(response.exception.message, 'test_error_message'); diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart index 01af03b1abbc..49d84ff88f88 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_html_test.dart @@ -17,7 +17,6 @@ final html.File textFile = html.File([bytes], 'hello.txt'); final String textFileUrl = html.Url.createObjectUrl(textFile); void main() { - group('Create with an objectUrl', () { final pickedFile = PickedFile(textFileUrl); @@ -33,7 +32,8 @@ void main() { }); test('Stream can be sliced', () async { - expect(await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); + expect( + await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); }); }); } diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart index aa9986cce91f..94ff759a2fb2 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart @@ -17,7 +17,6 @@ final File textFile = File('./assets/hello.txt'); final String textFilePath = textFile.path; void main() { - group('Create with an objectUrl', () { final pickedFile = PickedFile(textFilePath); @@ -33,7 +32,8 @@ void main() { }); test('Stream can be sliced', () async { - expect(await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); + expect( + await pickedFile.openRead(2, 5).first, equals(bytes.sublist(2, 5))); }); }); } From 1ccfb5152855ab08ac63dec03bb2c7309888005b Mon Sep 17 00:00:00 2001 From: David Iglesias Teixeira Date: Tue, 26 May 2020 12:17:17 -0700 Subject: [PATCH 7/7] Document default utf8 encoding in the base PickedFile. --- .../lib/src/types/picked_file/base.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart index 08afe6f1e41e..285294efcb3d 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/base.dart @@ -31,6 +31,8 @@ abstract class PickedFileBase { /// Synchronously read the entire file contents as a string using the given [Encoding]. /// + /// By default, `encoding` is [utf8]. + /// /// Throws Exception if the operation fails. Future readAsString({Encoding encoding = utf8}) { throw UnimplementedError('readAsString() has not been implemented.');