How can I get multiple messages from dart isolate?

Issue

How can I get multiple messages from dart isolate?

I’m trying to create an excel file and want to do some operation on that file in an isolate. Before doing an operation on that file, I want to return an message to main isolate, that excel file is created.

Here is function goes in isolate :

foo(String filePath){
    // create excel file
    var bytes = File(filePath).readAsBytesSync();
    var excel = Excel.decodeBytes(bytes);

    //HERE I WANT TO SEND THE MESSAGE THAT CREATING EXCEL FILE IS DONE

    // some operatoin on excel file
    var result = doSomeOperation(excel);
    return result;
}

Main isolate code :

var result = await compute(foo, filePath);

What should I do to get creating file message before the actual result comes?

For excel, I’m using excel: ^2.0.0-null-safety-3 package.

Solution

Compute only returns one result. If you want to pass multiple ‘events’ back to the main isolate then you need to use the full Isolate logic (with sendPort and receivePort).

For example, the following code runs in an isolate, and downloads a file while emitting float values to represent progress, potentially a String to indicate log messages and then a bool to indicate success or failure upon completion.

  Future<void> isolateDownload(
      DownloadRequest request) async {
    final sendPort = request.sendPort;
    if (sendPort != null) {
      var success = false;
      var errorMessage = '';
      var url = Uri.parse('a_url_based_on_request');
      IOSink? out;
      try {
        http.StreamedResponse response =
            await http.Client().send(http.Request('GET', url));
        if (response.statusCode == 200) {
          var filePath =
              join(request.destinationDirPath, '${request.fileName}.ZIP');
          var contentLength = response.contentLength;
          var bytesLoadedUpdateInterval = (contentLength ?? 0) / 50;
          var bytesLoaded = 0;
          var bytesLoadedAtLastUpdate = 0;
          out = File(filePath).openWrite();
          await response.stream.forEach((chunk) {
            out?.add(chunk);
            bytesLoaded += chunk.length;
            // update if enough bytes have passed since last update
            if (contentLength != null &&
                bytesLoaded - bytesLoadedAtLastUpdate >
                    bytesLoadedUpdateInterval) {
              sendPort.send(bytesLoaded / contentLength);
              bytesLoadedAtLastUpdate = bytesLoaded;
            }
          });
          success = true;
          if (contentLength != null) {
            sendPort.send(1.0); // send 100% downloaded message
          }
        } else {
          errorMessage =
              'Download of ${request.fileName} '
              'received response ${response.statusCode} - ${response.reasonPhrase}';
        }

      } catch (e) {
        errorMessage = 'Download of ${request.chartType}:${request.chartName} '
            'received error $e';
      } finally {
        await out?.flush();
        await out?.close();
        if (errorMessage.isNotEmpty) {
          sendPort.send(errorMessage);
        }
        sendPort.send(success);
      }
    }
  }

The code that spawns the isolate then simply checks for the type of the message passed to it to determine the action.

  Future<bool> _downloadInBackground(
      DownloadRequest request) async {
    var receivePort = ReceivePort();
    request.sendPort = receivePort.sendPort;
    var isDone = Completer();
    var success = false;
    receivePort.listen((message) {
      if (message is double) {
        showUpdate(message);
      }
      if (message is String) {
        log.fine(message); // log error messages
      }
      if (message is bool) {
        success = message; // end with success or failure
        receivePort.close();
      }
    }, onDone: () => isDone.complete()); // wraps up
    await Isolate.spawn(isolateDownload, request);
    await isDone.future;
    return success;
  }

Answered By – Bram

Answer Checked By – Dawn Plyler (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.