Using flutter's compute method with image_picker

·

2 min read

Introduction

Loading multiple images with the image_picker plugin can be a memory intensive task and your app can be killed on Android if the device is low on memory. This is a guide on how to offload this task to a dart isolate without using Isolate.spawn.

Isolates in Dart have their own memory space and do not share memory with each other or the main app this makes them ideal for processing photos.

In Flutter 3.7, isolates got an update that made them easier to use in conjunction with the RootIsolateToken and BackgroundIsolateBinaryMessenger API's. These two are needed in order to know which background isolates are associated with the Flutter engine order to shut down associated platform channels.

Let's get right to the implementation:

PickImageFunction

Future<Result<String, String>> pickFileImageFromIsolate(
  List<Object> args,
) async {
  final token = args[0] as RootIsolateToken;

  final imageSourceIndex = args[1] as int;

  final imageSource = ImageSource.values.elementAt(imageSourceIndex);

  BackgroundIsolateBinaryMessenger.ensureInitialized(token);

  try {
    final imagePicker = ImagePicker();

    final image = await imagePicker.pickImage(
      maxWidth: 800,
      maxHeight: 600,
      source: imageSource,
      imageQuality: 50,
    );

    if (image == null) {
      return const Result.error("Failed to get image from gallery");
    }

    return Result.success(image.path);
  } on PlatformException catch (e) {
    logger.e("Failed to get image from gallery: ${e.message}");

    return const Result.error("Failed to get image from gallery");
  } catch (_) {
    return const Result.error("Failed to get image from gallery");
  }
}

The Result object returned from this function comes from the multiple_result package. It contains either the path of the picked image on success, or an error message on failure.

The args parameter is a list of arguments that should contain the following:

Objects like ImageSource can't be sent by isolates since mainy primitive types are allowed.

https://api.flutter.dev/flutter/dart-isolate/SendPort/send.html

Using Compute


final imageSourceIndex = ImageSource.values.indexOf(imageSource);

final token = RootIsolateToken.instance!;

final pickFileImageResult = await compute(pickFileImageFromIsolate, [
  token,
  imageSourceIndex,
]);

Wrapping Up

In the main app, compute your function and pass the required arguments to it. Two are required, the function name and the argument to the function being called in the new isolate. Since the argument can be any primitive type you can go with List<Object> or List<dynamic> if there's a need to send multiple objects.

Calling the compute function spins up an isolate, runs the callback function pickFileImageFromIsolate in that isolate, passing to it the required data, and then returns the result. Remember the RootIsolateToken is required and the function will not run without it.

Did you find this article valuable?

Support Henry's blog by becoming a sponsor. Any amount is appreciated!