Radoslav's Blog

Selecting multiple images with Flutter

Posted on January, 31 2019 in mobile flutter

Selecting multiple images with Flutter
Flutter Multi Image Picker

Last year I've decided to rewrite one of my published mobile apps (built with Ionic 1) using a more reliable framework. Ionic was kind of fine, but the breaking changes introduced with each new release and the limitations of the web view were consuming too much of my time maintaining the project.

I almost certainly was going to pick React Native, but a new trending framework got my attention - Flutter. Since I wanted to explore all options I've decided to try and build a sample application before I make my decision.

It turned out this was going to be my future-go-to mobile framework to build apps.


One thing that was missing was a plugin that allows picking multiple images from a gallery (when I started rewriting the project Flutter was just out of Alpha). I've even opened a SOS thread on Reddit hoping someone more fluent with Flutter will point me to a suitable plugin.

Unfortunately there wasn't one, so I've decided to create a new one.

Flutter Multi Image Picker

Today multi_image_picker has more than 130 stars on Github and thanks to the community it reached a stable state. It has key features like thumbnail and metadata support, ability to take a photo from the camera right from the gallery, specifying image quality and many more.

I am going to quickly show you how you can use this plugin in your project and give you some tips and tricks for great performance and example usage.

Installation

To add the plugin to your project just put the following as dependency in your pubspec.yaml file:

dependencies:
  multi_image_picker: ^3.0.11

It is important to mention here that since version 3 of the plugin, I am now using AndroidX instead of the deprecated Android Support Library, thus your project must use version 28 of the compileSdkVersion.

Building a simple gallery app

We will build a sample app that will let you pick as many as you wish images from your phone and display them in a scrollable gallery in Flutter


Let's start with the main.dart file.


It is a simple page. We've defined _images and _error state variables which will hold the result of the image picker. Based on that if we have an error we will render the error message in the middle of the screen, and if not error occurred, we will render a simple PageView displaying the images. More about that in a minute, now let's take a look how exactly we pick the images.

void _pickImages() async {
  List<Asset> resultList = List<Asset>();
  String error = '';

  try {
   resultList = await MultiImagePicker.pickImages(
    maxImages: 300,
   );
  } on PlatformException catch (e) {
   error = e.message;
  }

  if (!mounted) return;

  setState(() {
   _images = resultList;
   _error = error;
  });
 }

The _pickImages method is called when you tap the fab button. It will invoke the plugin method pickImages, which will return a list of Asset objects. Asset represents an image resource which have several handy methods you can invoke on it in order to obtain different data for the given image - requestThumbnail, requestOriginal, and requestMetadata . All those methods are asynchronious and return a Future.

We are also passing a maxImages parameter to indicate the maximum number of images that the user is allowed to pick. This method supports also additional number of options like enableCamera (which will show a camera icon in the gallery and allow the user to take pictures and then select them as well) and options (which allows you to tweak the appearance of the image picker).

Now that we have the list of images stored in the state, lets see how we can display them.

This is the content of the asset_view.dart


This is a StatefulWidget which we will use in our PageView.builder method. It accepts and Asset object and a key. I am not going to explain this article what keys are, but in general when you are providing a key to a widget it can properly track changes to its state.

In our example app we want to display a list with thumbnails. We don't want the original images as they may be too large. Actually the only time you want to request the original image is when you are uploading it to your API, or are manipulating it in some way like a cropper. Rest of the time of you want to display the selected images in your app, I suggest you to use thumbs with appropriate dimensions. So let's take a look at the build method of out AssetView:

@override
 Widget build(BuildContext context) {
  if (null != this._asset.thumbData) {
   return Image.memory(
    this._asset.thumbData.buffer.asUint8List(),
    fit: BoxFit.cover,
    gaplessPlayback: true,
   );
  }

  return Center(
   child: CircularProgressIndicator(),
  );
 }

As I said earlier the Asset object is just a wrapper around the image resource on the user device. We need to check first if the asset have a thumb data already loaded. If it does not we show a nice spinning wheel until it is loaded so the user don't see a white or jumping screen meanwhile. When the thumb data is available we will display an Image widget that show the given image.

Before we see how thumb data is loaded, let's first take a moment to understand what thumbData is. Both thumbData and imageData getters return a ByteData . Once requested it will be stored in the asset object for further usage. Since it lives in the memory it is a good idea to release it when it is no longer needed, e.g if you navigate to other page or upload it to a server. To do that you can use the release() method on the asset object.

So how we request the thumbnail?

@override
 void initState() {
  super.initState();
  _loadImage();
 }

 void _loadImage() async {
  await this._asset.requestThumbnail(300, 300, quality: 50);

  if (this.mounted) {
   setState(() {});
  }
 }

We do that in the initState of out StatefulWidget. In the above example we are invoking the requestThumbnail method with the desired width and height, and we can pass on optional parameter for the quality (compression) of the image. Once this method finishes it will load the thumb data in the asset class for further usage. It will be accessible through asset.thumbData. After that we are just refreshing the state so the build method will be triggered again and this time it will see there is a thumb data and will render the image.

In the next blog post I will show you how you can upload assets to a server and how you can request image metadata.

By Radoslav Vitanov

I am a Full Stack developer, experienced with building scalable web applications with Laravel, performant single page applications with React and mobile apps with Flutter.