0
votes

I am using the image_picker library (https://pub.dartlang.org/packages/image_picker#-readme-tab-) to allow a user to select a profile image from their photo library/gallery. On iOS, the user can only access photos in their photos, but on Android a user can go to File Manager and select a different file type such as audio. Simply checking for null, does not guarantee that the user has selected an image.

Is there a way in Dart/Flutter to ensure that _imageFile is an image that will successfully work with the FileImage constructor?

Future getImage() async {

    if(currentUser.isLoggedIn) {
      _imageFile = await ImagePicker.pickImage(source: ImageSource.gallery);
      setState(() {

        if (_imageFile != null) {
          currentUser.profileImage = FileImage(_imageFile);
          uploadImage();
        } else {
          print('no image selected');
        }
      });
    }

Here is the error report when selecting an audio file from File Manager:

E/flutter (14947): [ERROR:flutter/lib/ui/painting/codec.cc(97)] Failed decoding image. Data is either invalid, or it is encoded using an unsupported format.
I/flutter (14947): ══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞════════════════════════════════════════════════════
I/flutter (14947): The following _Exception was thrown resolving an image codec:
I/flutter (14947): Exception: operation failed
I/flutter (14947): 
I/flutter (14947): When the exception was thrown, this was the stack:
I/flutter (14947): #0      FileImage._loadAsync (package:flutter/src/painting/image_provider.dart:579:12)
I/flutter (14947): <asynchronous suspension>
I/flutter (14947): #1      FileImage.load (package:flutter/src/painting/image_provider.dart:564:14)
I/flutter (14947): #2      ImageProvider.resolve.<anonymous closure>.<anonymous closure> (package:flutter/src/painting/image_provider.dart:299:46)
I/flutter (14947): #3      ImageCache.putIfAbsent (package:flutter/src/painting/image_cache.dart:157:22)
I/flutter (14947): #4      ImageProvider.resolve.<anonymous closure> (package:flutter/src/painting/image_provider.dart:299:23)
I/flutter (14947): #5      SynchronousFuture.then (package:flutter/src/foundation/synchronous_future.dart:38:29)
I/flutter (14947): #6      ImageProvider.resolve (package:flutter/src/painting/image_provider.dart:296:9)
I/flutter (14947): #7      DecorationImagePainter.paint (package:flutter/src/painting/decoration_image.dart:239:55)
I/flutter (14947): #8      _BoxDecorationPainter._paintBackgroundImage (package:flutter/src/painting/box_decoration.dart:414:19)
I/flutter (14947): #9      _BoxDecorationPainter.paint (package:flutter/src/painting/box_decoration.dart:432:5)
I/flutter (14947): #10     RenderDecoratedBox.paint (package:flutter/src/rendering/proxy_box.dart:1968:16)
I/flutter (14947): #11     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2104:7)
I/flutter (14947): #12     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:173:13)
I/flutter (14947): #13     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:123:15)
I/flutter (14947): #14     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2104:7)
I/flutter (14947): #15     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:173:13)
I/flutter (14947): #16     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:123:15)
I/flutter (14947): #17     RenderPointerListener.paint (package:flutter/src/rendering/proxy_box.dart:2645:11)
I/flutter (14947): #18     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2104:7)
I/flutter (14947): #19     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:173:13)
I/flutter (14947): #20     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:123:15)
I/flutter (14947): #21     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2104:7)
I/flutter (14947): #22     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:173:13)
I/flutter (14947): #23     RenderShiftedBox.paint (package:flutter/src/rendering/shifted_box.dart:70:15)
I/flutter (14947): #24     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2104:7)
I/flutter (14947): #25     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:173:13)
I/flutter (14947): #26     _RenderStack&RenderBox&ContainerRenderObjectMixin&RenderBoxContainerDefaultsMixin.defaultPaint (package:flutter/src/rendering/box.dart:2273:15)
I/flutter (14947): #27     RenderStack.paintStack (package:flutter/src/rendering/stack.dart:594:5)
I/flutter (14947): #28     RenderStack.paint (package:flutter/src/rendering/stack.dart:602:7)
I/flutter (14947): #29     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2104:7)
I/flutter (14947): #30     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:173:13)
I/flutter (14947): #31     RenderShiftedBox.paint (package:flutter/src/rendering/shifted_box.dart:70:15)
I/flutter (14947): #32     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2104:7)
I/flutter (14947): #33     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:173:13)
I/flutter (14947): #34     _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:123:15)
I/flutter (14947): #35     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2104:7)
I/flutter (14947): #36     PaintingContext._repaintCompositedChild (package:flutter/src/rendering/object.dart:128:11)
I/flutter (14947): #37     PaintingContext.repaintCompositedChild (package:flutter/src/rendering/object.dart:96:5)
I/flutter (14947): #38     PipelineOwner.flushPaint (package:flutter/src/rendering/object.dart:859:29)
I/flutter (14947): #39     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:338:19)
I/flutter (14947): #40     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:701:13)
I/flutter (14947): #41     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:275:5)
I/flutter (14947): #42     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1008:15)
I/flutter (14947): #43     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:948:9)
I/flutter (14947): #44     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:860:5)
I/flutter (14947): #48     _invoke (dart:ui/hooks.dart:219:10)
I/flutter (14947): #49     _drawFrame (dart:ui/hooks.dart:178:3)
I/flutter (14947): (elided 3 frames from package dart:async)
I/flutter (14947): 
I/flutter (14947): Path: /data/user/0/com.undauntedathlete.UndauntedGolf/cache/image_picker2760241735413495899jpg
I/flutter (14947): ════════════════════════════════════════════════════════════════════════════════════════════════════
1
doesn't the image_picker package do checking directly out of the box?flutter
@flutter No it does not. I just uploaded the crash report to the original post.Eric Duffett

1 Answers

1
votes

imho this is very much a bug in the file manager you are using, because the image_picker plugin does request pickImageIntent.setType("image/*"); (And for example the Google Drive provider does only allow selecting images).

So basically you have to check it yourself. I figure there are three methods, depending on how sophisticated you require the solution to be.

  1. the low-quality check by simply checking the File.path whether it ends in a known image file extension.. (you could use e.g. the mime dart package which already contains a good list of extensions.
  2. a bit hackish: after retrieving the File, use the ImageFile.resolve to try to resolve the image, before setting it on the Image itself (catching the errors and warning the user)
  3. a bit more work, but probably the cleanest solution: when displaying using Image() use a custom ImageProvider, and when resolving the image catch the error and handle it appropriately.

It kind of depends on your requirements obviously, if you let the user select an image and imediately upload it, nr 2. would make the most sense. If you first let the user preview the image use nr 3. -- Or maybe if you just upload it, it would even make sense to let the user upload the image, and make a server side check. So for example a user can select SVGs, which the server might be able to handle, but flutter can't (out of the box anyway).