Use Isolates to prevent UI Jank | Flutter multiprocessing
Tags: flutter, dart, android
Published: 2022-08-21 18:38:00

As you might know flutter runs asynchronously on a single thread often called the main thread and also where the UI is running, now when you need to run heavy computational function you find that you app freezes and janks because is busy with that function.

Here is where Isolates come into action, this is a way to spawn threads in dart/flutter and prevent the main thread to halt.

Isolates are a little finicky but they are useful when the situation comes and this was the perfect situation, I needed to load an image and send it to an API for processing, this was janking the main thread but it can be done on an isolate and prevent that.

Let's see one of the easiest ways of using isolates with the compute function
## Function to run
First we need a function to execute, this function loads the image and sends it with http to the API.

```dart
Future<Uint8List?> requestImg(RequestInputs reqInputs) async {
  File file = File(reqInputs.filePath!);

  final http.MultipartRequest request = http.MultipartRequest(
    'POST',
    Uri.parse('server:uri'),
  );
  final Map<String, String> headers = <String, String>{'Content-type': 'multipart/form-data'};

  final img_proc.Image tmp = img_proc.decodeImage(await file.readAsBytes())!;
  final List<int> tosend = img_proc.encodeJpg(tmp);

  request.files.add(http.MultipartFile.fromBytes(
    'file',
    tosend,
    filename: 'aism_source',
    contentType: MediaType('image', 'jpeg'),
  ));

  request.headers.addAll(headers);
  request.fields.addAll(reqInputs.settings);
  final http.StreamedResponse res = await request.send();
  debugPrint('Request settings sent');

  Uint8List responseImage = await res.stream.toBytes();
  return responseImage;
}
```

Now my function needed more than one argument but the compute function only accepts functions with one, to solve this I use a custom class to pass all of the data.

```dart
class RequestInputs {
  final String? filePath;
  final Map<String, String> settings;

  RequestInputs(this.filePath, this.externalPath, this.settings);
}
```

## Compute function
Great we have the function and the custom class let's see how to use compute to run it on an isolate.

```dart
// #1
compute<StickerRequestInputs, Uint8List?>(
  requestImg,
  StickerRequestInputs(
    imgPath,
    storage,
    settings,
  ),
// #2
).then((value) => setState(() {
      loading = false;
      if (value != null) {
        imgBytes = value;
        saveImage(value);
      } else {
        debugPrint('Error!');
      }
    }));
```
We first need to declare the function input and output types in the compute statement #1 `compute<Q,R>` Q is the input type and R is the return type.

Then we pass the function and the input to run, in this case we  instantiate the custom class that we declared.

The compute is a future of the return type R #2, we can use either async or .then to do something after it finishes, in this case we update the state with the new data or we print an error if it fails.

Watch the video for a walkthrough of all the code, subscribe to see the progress of the app :D
{% embed https://youtu.be/6GVYe6vI5hI %}

Alright and that's it, I will continue working with isolates and see what we can do with them, but as i said the are a little tricky to work with regardless they are a good option when you need them.