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

[image_picker_for_web] Introduce image_picker_for_web package #2802

Merged
merged 9 commits into from
Jun 3, 2020

Conversation

ditman
Copy link
Member

@ditman ditman commented May 29, 2020

Description

This is a first version of an Image Picker plugin for web.

Since dart:io File is not available on the web, this only implements the new PickedFile methods from the platform interface.

Instead of storing a "normal" path value on the file, however, it stores an "object URL" (see createObjectUrl) that the browser keeps track of. The PickedFiles in the web platform can be accessed through a network request (not a file operation).

Demo

https://dit-picker-tests.web.app

Known issues

Related Issues

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • My PR includes unit or integration tests for all changed/updated/fixed behaviors (See Contributor Guide).
  • All existing and new tests are passing.
  • I updated/added relevant documentation (doc comments with ///).
  • The analyzer (flutter analyze) does not report any problems on my PR.
  • I read and followed the Flutter Style Guide.
  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy.
  • I updated CHANGELOG.md to add a description of the change.
  • I signed the CLA.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Does your PR require plugin users to manually update their apps to accommodate your change?

  • No, this is an optional plugin, and users don't need to use it if they don't want to.

@ditman ditman requested review from yjbanov and ferhatb June 2, 2020 22:47
@ditman
Copy link
Member Author

ditman commented Jun 2, 2020

I think I addressed all the comments, @ferhatb, @yjbanov, PTAL if you have some time, thanks!

Copy link
Contributor

@harryterkelsen harryterkelsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM although I would prefer to return an ImageProvider rather than a PickedFile. This way, the developer could just do Image(pickedImage) whether you are on web or not.

See: https://api.flutter.dev/flutter/widgets/Image/Image.html

@ditman
Copy link
Member Author

ditman commented Jun 3, 2020

Mostly LGTM although I would prefer to return an ImageProvider rather than a PickedFile. This way, the developer could just do Image(pickedImage) whether you are on web or not.

See: https://api.flutter.dev/flutter/widgets/Image/Image.html

@hterkelsen I looked a little bit into this, and the issue I encountered was that only Images can be created that way; there's no concept of VideoProvider, AudioProvider, "AssetProvider" or FileProvider (AFAIK), so that solution would only cover a fraction of the use cases of the plugin (I didn't measure what's the actual breakdown of usage, it might be a large fraction of the use cases :P)

PS: Now that web and desktop are coming, there's already feature requests of "let me pick any arbitrary file from my disk.

In any case, I think that's a very good idea for an extra API method down the line; if you're only using the plugin for Images, we can add a "PickedImageProvider" class that lets you create Image widgets directly! (It'd still require dart:io vs dart:html implementations, but they'd be doable). Do you think we need an issue to track that task (P5/P6?), or wait until some user asks for something like that explicitly?

@harryterkelsen
Copy link
Contributor

Ah, that makes sense. I don't think we need to add it in that case

Copy link
Contributor

@cyanglaz cyanglaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ditman ditman merged commit e987d25 into flutter:master Jun 3, 2020
@ditman ditman deleted the image-picker-for-web branch June 3, 2020 23:41
@ditman
Copy link
Member Author

ditman commented Jun 4, 2020

Published to pub.dev:

https://pub.dev/packages/image_picker_for_web (^0.1.0)

@parammittal162
Copy link

parammittal162 commented Jun 5, 2020

hi I am stuck in some issues regarding file validation, i.e. how can we check is user has uploaded only image extension file on web, because user can select option "All files" and upload any file on web.

if (kIsWeb) {
  Image.network(pickedFile.path);
} else {
  Image.file(File(pickedFile.path));
}

like here for web if I console the Image I am getting -

Image(image: NetworkImage("blob:http://0.0.0.0:55555/316c9cde-1333-4294-a72d-03e45d97f5f7", scale: 1), frameBuilder: null, loadingBuilder: null, alignment: center, this.excludeFromSemantics: false, filterQuality: low)

So,

blob:http://0.0.0.0:55555/316c9cde-1333-4294-a72d-03e45d97f5f7

does not contain any file extension info or meta data kind something ?? Please Help ...

@ditman
Copy link
Member Author

ditman commented Jun 5, 2020

@parammittal162 the only reliable way of doing what you want is either doing it via server-side, or by looking at the bytes of the PickedFile and trying to recognize the header of the image.

You would be in a similar situation if your user renames "file.txt" to "file.jpg" and tries to pick it.

@parammittal162
Copy link

ok Thanks @ditman

@markgrancapal
Copy link

@andrea689 thanks for the help!

@ditman
Copy link
Member Author

ditman commented Jun 11, 2020

We probably need to add a file metadata attribute to the PickedFile object. It'll be redundant in mobile, but useful for web. Thanks for the feedback!!

@m-j-g
Copy link

m-j-g commented Jun 11, 2020

@ditman thanks for all your work on this. I've been messing with 0.6.7+1 and converted my implementation. I switched to bytes so that it would work on both web and mobile.
final _picker = ImagePicker();

    try {
      final pickedFile = await _picker.getImage(
        source: ImageSource.gallery,
        maxWidth: 400,
        maxHeight: 400,
        imageQuality: 80,
      );
      final result = await pickedFile.readAsBytes();

      bloc.updateImageSelection(result);
    } catch (e) {
      Log.d(e.toString());
    }

Works fine on mobile.
However, on web I never make it to an image selection. As soon as I run this code I receive >
MissingPluginException(No implementation found for method pickImage on channel plugins.flutter.io/image_picker)

I'm assuming that I missed something, and tips would be appreciated.

@ditman
Copy link
Member Author

ditman commented Jun 11, 2020

"No implementation found for method pickImage on channel plugins.flutter.io/image_picker"

@m-j-g The image_picker_for_web plugin is still not officially endorsed, so it will not be added to your dependencies automatically. You probably need to update your pubspec.yaml and depend on it, like so:

image_picker: ^0.6.7
image_picker_for_web: ^0.1.0

Once the plugin has been adopted by more people and we iron out whatever issues/features come up, we'll endorse it, so you just need to depend on a recent version of image_picker. For now, please add the dependency on the web version to your app!

Thanks for giving this a shot!

PS: check the README in the web version of the plugin, it has documentation about installation, browser support, etc...

@m-j-g
Copy link

m-j-g commented Jun 11, 2020

@ditman
ahh thanks. I kept seeing that package, but my mind dismissed it as transitive. Thanks for clearing that up!

@ditman
Copy link
Member Author

ditman commented Jun 11, 2020

@m-j-g makes sense, almost every other plugin "hides" the web implementation so you don't have to worry about it. Note that every web plugin has (or should have) a similar README, with installation instructions and "quirks" of the web platform.

@markgrancapal
Copy link

@markgrancapal you can use findDecoderForData() of package image

It worked! Thanks! But now I need to know the file extension for videos, and I can't find any suitable package for this one. Any suggestion? Thank you!

@markgrancapal
Copy link

We probably need to add a file metadata attribute to the PickedFile object. It'll be redundant in mobile, but useful for web. Thanks for the feedback!!

a way to know the filename or even the extension would be a big help for sure.

image_picker_web does this but the problem is it doesn't provide a network path. For videos I can't find a way to play a video using Byte data or Html.File

@shujaatak
Copy link

@ditman Can you please share the link of the source code of the Demo:
https://dit-picker-tests.web.app

@ditman
Copy link
Member Author

ditman commented Sep 3, 2020

@nonnyzcsrt
Copy link

@ditman I used method pickImage found that image not resizing and compressing follow maxWidth maxHeight and imageQuality. Please suggest me. Thank you
Screen Shot 2563-10-06 at 00 49 34

@ditman
Copy link
Member Author

ditman commented Oct 5, 2020

@nonnyzcsrt resizing on the fly is not supported on web, for now. You might be able to load the image bytes in a canvas and then do the resizing, but this is not natively supported!

jorgefspereira pushed a commit to jorgefspereira/plugins_flutter that referenced this pull request Oct 10, 2020
FlutterSu pushed a commit to FlutterSu/flutter-plugins that referenced this pull request Nov 20, 2020
@postacik
Copy link

postacik commented Jan 14, 2021

If someone ever gets here before "image resizing functionality" is implemented in the package, you can use the following function to resize an image in the browser:

import 'dart:html' as html;

  static Future<String> resizeImage(String src, int maxWidth, int maxHeight, int imageQuality) {
    var completer = Completer<String>();
    var img = html.ImageElement();
    img.onError.listen((event) {
      completer.complete("");
    });
    img.onLoad.listen((event) {
      var canvas = html.CanvasElement();
      var ctx = canvas.context2D;
      double ratio = 1;
      print("Original size: ${img.width} x ${img.height}");
      if (img.width > img.height) {
        if (img.width > maxWidth) ratio = maxWidth / img.width;
      } else if (img.height > maxWidth) {
        ratio = maxWidth / img.height;
      }
      if (img.width > img.height) {
        if (img.width < maxHeight) ratio = maxHeight / img.width;
      } else if (img.height < maxHeight) {
        ratio = maxHeight / img.height;
      }
      print("Resizing $ratio times");
      canvas.height = (img.height * ratio).floor();
      canvas.width = (img.width * ratio).floor();
      // Draw the image to canvas and resize
      ctx.drawImageScaled(img, 0, 0, canvas.width, canvas.height);
      var base64 = canvas.toDataUrl("image/png", imageQuality / 100);
      completer.complete(base64);
    });
    img.src = src;
    // make sure the load event fires for cached images too
    if (img.complete) {
      // Flush cache
      img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==';
      // Try again
      img.src = src;
    }
    return completer.future;
  }

@ditman
Copy link
Member Author

ditman commented Jan 14, 2021

@postacik this is a cool example, why not add it to the plugin code itself?

@postacik
Copy link

@ditman , I am not familiar with the flutter plugins structure. It would be great if someone else could add it to the plugin.

@jesusrp98
Copy link

I'll do @ditman @postacik.
I'll open a PR later today containing this code!

@jesusrp98
Copy link

The thing that I was thinking about is that the code above is only meant for. Would it be better to make a code that runs on any platform??

@postacik
Copy link

    final _picker = ImagePicker();
    final PickedFile pickedFile = await _picker.getImage(source: ImageSource.gallery, maxWidth: 400, maxHeight: 400, imageQuality: 80);

If this code is run on iOS or Android it resizes the selected image with the given size parameters and image quality.

However on web the maxWidth, maxHeight and imageQuality parameters have no effect. The image is returned as is.

If you add this code to the web platform implementation, it will be behaving just like the mobile platforms.

@ditman , please correct me if I'm wrong.

@ditman
Copy link
Member Author

ditman commented Jan 15, 2021

@jesusrp98 @postacik yes, I think the native picker is already able to resize images both on iOS and Android. This is a missing feature of Flutter web.

Please @jesusrp98, remember to create a Flutter issue for [image_picker_for_web] that explains what you're going to do :)

@postacik the code for picking images in web is here, in case you want to lend a hand to @jesusrp98 :) https://github.com/flutter/plugins/blob/master/packages/image_picker/image_picker_for_web/lib/image_picker_for_web.dart

@postacik
Copy link

postacik commented Jan 16, 2021

By the way, ImageSource.camera has no effect on desktop. It works only on mobile.

WhatsApp web uses browser WebRTC functions to capture image from webcam.

There's a flutter library called flutter_webrtc which has web support and can capture image from webcam.

Maybe we can copy the web implementation from that library to support ImageSource.camera on desktop.

But this feature would need to push a new screen to the navigator which will allow the user to capture an image and then edit the captured image which is similar to the native code.

Would you prefer such a feature on web?

2021-01-16-13-45-35.mp4

@jesusrp98
Copy link

Planning to open the issue tomorrow if I have the time @ditman.

Also @postacik, I think it would be awesome to implement that feature directly into image_picker.

@ditman
Copy link
Member Author

ditman commented Jan 19, 2021

We're soon going to work on the camera plugin for web, which should implement this specific feature. Then (maybe) depend on the camera plugin for the ImageSource.camera on desktop browsers?

@Joseph-Nathan
Copy link

any news for provide ImageSource.camera on desktop browsers ?

@ditman
Copy link
Member Author

ditman commented Jul 6, 2022

@Joseph-Nathan no, there's a whole bunch of UX that needs to be built around the camera plugin that doesn't exist. A default widget to "take the photo" would be needed, and AFAIK that doesn't exist, even in the camera plugin (the only provided widget is a "preview" of the camera feed)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.