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

[image_picker] 🚧 Add support for web 🚧 #2767

Closed
wants to merge 15 commits into from
Closed
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
4 changes: 4 additions & 0 deletions packages/image_picker/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.6.7

* Utilize the new platform_interface package.

## 0.6.6+4

* Fix bug, sometimes double click cancel button will crash.
Expand Down
45 changes: 31 additions & 14 deletions packages/image_picker/image_picker/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/basic.dart';
import 'package:flutter/src/widgets/container.dart';
Expand Down Expand Up @@ -37,20 +38,25 @@ class MyHomePage extends StatefulWidget {
}

class _MyHomePageState extends State<MyHomePage> {
File _imageFile;
PickedFile _imageFile;
dynamic _pickImageError;
bool isVideo = false;
VideoPlayerController _controller;
String _retrieveDataError;

final ImagePicker _picker = ImagePicker();
final TextEditingController maxWidthController = TextEditingController();
final TextEditingController maxHeightController = TextEditingController();
final TextEditingController qualityController = TextEditingController();

Future<void> _playVideo(File file) async {
Future<void> _playVideo(PickedFile file) async {
if (file != null && mounted) {
await _disposeVideoController();
_controller = VideoPlayerController.file(file);
if (kIsWeb) {
_controller = VideoPlayerController.network(file.path);
} else {
_controller = VideoPlayerController.file(File(file.path));
}
await _controller.setVolume(1.0);
await _controller.initialize();
await _controller.setLooping(true);
Expand All @@ -64,21 +70,26 @@ class _MyHomePageState extends State<MyHomePage> {
await _controller.setVolume(0.0);
}
if (isVideo) {
final File file = await ImagePicker.pickVideo(
final PickedFile file = await _picker.getVideo(
source: source, maxDuration: const Duration(seconds: 10));
await _playVideo(file);
} else {
await _displayPickImageDialog(context,
(double maxWidth, double maxHeight, int quality) async {
try {
_imageFile = await ImagePicker.pickImage(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality);
setState(() {});
final pickedFile = await _picker.getImage(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: quality,
);
setState(() {
_imageFile = pickedFile;
});
} catch (e) {
_pickImageError = e;
setState(() {
_pickImageError = e;
});
}
});
}
Expand Down Expand Up @@ -132,7 +143,13 @@ class _MyHomePageState extends State<MyHomePage> {
return retrieveError;
}
if (_imageFile != null) {
return Image.file(_imageFile);
if (kIsWeb) {
return Image.network(_imageFile.path);
// Or from memory...
} else {
// This would also work from memory as well...
return Image.file(File(_imageFile.path));
}
} else if (_pickImageError != null) {
return Text(
'Pick image error: $_pickImageError',
Expand All @@ -147,7 +164,7 @@ class _MyHomePageState extends State<MyHomePage> {
}

Future<void> retrieveLostData() async {
final LostDataResponse response = await ImagePicker.retrieveLostData();
final LostData response = await _picker.getLostData();
if (response.isEmpty) {
return;
}
Expand All @@ -173,7 +190,7 @@ class _MyHomePageState extends State<MyHomePage> {
title: Text(widget.title),
),
body: Center(
child: Platform.isAndroid
child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android
? FutureBuilder<void>(
future: retrieveLostData(),
builder: (BuildContext context, AsyncSnapshot<void> snapshot) {
Expand Down
2 changes: 1 addition & 1 deletion packages/image_picker/image_picker/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ dependencies:
video_player: ^0.10.3
flutter:
sdk: flutter
flutter_plugin_android_lifecycle: ^1.0.2
image_picker:
path: ../
flutter_plugin_android_lifecycle: ^1.0.2

dev_dependencies:
flutter_driver:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions packages/image_picker/image_picker/example/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="An example of the image_picker on the web.">

<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">

<!-- Favicon -->
<link rel="shortcut icon" type="image/png" href="favicon.png"/>

<title>url_launcher web example</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<!-- <script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script> -->
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
23 changes: 23 additions & 0 deletions packages/image_picker/image_picker/example/web/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "image_picker example",
"short_name": "image_picker",
"start_url": ".",
"display": "minimal-ui",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "An example of the image_picker on the web.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
89 changes: 89 additions & 0 deletions packages/image_picker/image_picker/lib/image_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package

import 'dart:async';
import 'dart:io';

Expand All @@ -15,7 +17,9 @@ export 'package:image_picker_platform_interface/image_picker_platform_interface.
kTypeVideo,
ImageSource,
CameraDevice,
LostData,
LostDataResponse,
PickedFile,
RetrieveType;

/// Provides an easy way to pick an image/video from the image library,
Expand Down Expand Up @@ -47,6 +51,7 @@ class ImagePicker {
///
/// 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.
@Deprecated('Use imagePicker.getImage() method instead.')
static Future<File> pickImage(
{@required ImageSource source,
double maxWidth,
Expand All @@ -64,6 +69,44 @@ class ImagePicker {
return path == null ? null : File(path);
}

/// Returns a [PickedFile] object wrapping the image that was picked.
///
/// The returned [PickedFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions.
///
/// 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 [getLostData] when your app relaunches to retrieve the lost data.
Future<PickedFile> getImage({
@required ImageSource source,
double maxWidth,
double maxHeight,
int imageQuality,
CameraDevice preferredCameraDevice = CameraDevice.rear,
}) {
return platform.pickImage(
source: source,
maxWidth: maxWidth,
maxHeight: maxHeight,
imageQuality: imageQuality,
preferredCameraDevice: preferredCameraDevice,
);
}

/// Returns a [File] object pointing to the video that was picked.
///
/// The returned [File] is intended to be used within a single APP session. Do not save the file path and use it across sessions.
Expand All @@ -80,6 +123,7 @@ class ImagePicker {
///
/// 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.
@Deprecated('Use imagePicker.getVideo() method instead.')
static Future<File> pickVideo(
{@required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Expand All @@ -93,6 +137,34 @@ class ImagePicker {
return path == null ? null : File(path);
}

/// Returns a [PickedFile] object wrapping the video that was picked.
///
/// The returned [PickedFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions.
///
/// 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.
Future<PickedFile> getVideo({
@required ImageSource source,
CameraDevice preferredCameraDevice = CameraDevice.rear,
Duration maxDuration,
}) {
return platform.pickVideo(
source: source,
preferredCameraDevice: preferredCameraDevice,
maxDuration: maxDuration,
);
}

/// Retrieve the lost image 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.
Expand All @@ -109,4 +181,21 @@ class ImagePicker {
static Future<LostDataResponse> retrieveLostData() {
return platform.retrieveLostDataAsDartIoFile();
}

/// Retrieve the lost [PickedFile] when [selectImage] or [selectVideo] 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<LostData> getLostData() {
return platform.retrieveLostData();
}
}
9 changes: 7 additions & 2 deletions packages/image_picker/image_picker/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: image_picker
description: Flutter plugin for selecting images from the Android and iOS image
library, and taking new pictures with the camera.
homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker
version: 0.6.6+4
version: 0.6.7

flutter:
plugin:
Expand All @@ -12,12 +12,17 @@ flutter:
pluginClass: ImagePickerPlugin
ios:
pluginClass: FLTImagePickerPlugin
web:
default_package: image_picker_for_web

dependencies:
flutter:
sdk: flutter
flutter_plugin_android_lifecycle: ^1.0.2
image_picker_platform_interface: ^1.0.0
image_picker_platform_interface:
path: ../image_picker_platform_interface
image_picker_for_web:
path: ../image_picker_for_web

dev_dependencies:
video_player: ^0.10.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package

import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:image_picker/image_picker.dart';
Expand Down
3 changes: 3 additions & 0 deletions packages/image_picker/image_picker_for_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# 0.1.0

* Initial open-source release.
27 changes: 27 additions & 0 deletions packages/image_picker/image_picker_for_web/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Loading