4

I am a new in Flutter, so the question can be kind of obvious, but I can't find any answer on the Internet.

I have a Flutter application with some screens and I would say on the fifth screen I have a button, which should trigger some heavy computation work (converting thousands of images). On the same screen there is a progress bar and it is supposed to display the progress.

I am puzzled how to implement that technically. The triggering is happening obviously on onPressed of the button.

  • if I simply call a Future<void> function, then the UI is freezing completely for the time of processing, which is obviously is not desired behavior
  • if I put my function inside compute, on the first await I get exception Unhandled Exception: Exception: ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized. If you're running an application and need to access the binary messenger before runApp() has been called (for example, during plugin initialization), then you need to explicitly call the WidgetsFlutterBinding.ensureInitialized() first. which puzzles me, because I call WidgetsFlutterBinding.ensureInitialized() before runApp(). Anyway this method is not working.
compute(computationFunction, 'argument');
// ...
static void computationFunction(String argument) async {
  await firstStepFunction();
// ...
  • if I put my function into Isolate.spawn I get exception Unhandled Exception: Invalid argument(s): Isolate.spawn expects to be passed a static or top-level function which is also puzzling me. I tried to make the function static and moved the function to the top level of this fifth screen module. Nothing changed. Am I supposed to start the Isolate at the main function? In all beautiful examples it is done like that. Can't I start the Isolate in the middle by the button click.
Isolate.spawn(computationFunction, receivePort.sendPort);
// ...
void computationFunction(SendPort sendPort) async {
  await firstStepFunction();
// ...

In Java I think a simple new Thread(...).start() will do the job.

But how to do it in Flutter?


Update:

In my experiments I've noticed, that neither Flutter Hot Restart nor Hot Reload are not working correctly with isolates. You need really to run again the whole app.

I managed to start Isolate.spawn all right if async/await keywords are removed. Off course the called function should have its synchronous version. So this does not work universally.

Isolate.spawn(computationFunction, receivePort.sendPort);
// ...
static void computationFunction(SendPort sendPort) { // async removed
  firstStepFunctionSync(); // the function is replaced with its synchronous version 
// ...

I've found package flutter_isolate which allows to run the async functions:

FlutterIsolate.spawn(computationFunction, argument);
// ...
void computationFunction(SendPort sendPort) async {
  await firstStepFunction();
// ...

I will try to use flutter_isolate package in my prototype.

mspnr
  • 416
  • 4
  • 12

2 Answers2

3

You should read https://dev.to/alphamikle/why-should-you-use-isolates-in-flutter-1k5o, and look at package:isolates.

The article contrasts using main thread, compute, Isolate proper, and the isolates package, with advantages and disadvantages of each. Best article I've seen in a long time.

Also keep in mind, Java threads are data-shared, leading to possible deadlocks. Dart isolates are share-nothing, using "ports" to carefully move data between isolates, and no need for locking!

Randal Schwartz
  • 39,428
  • 4
  • 43
  • 70
0

Check out this plugin, which provides an easy way to work with isolates with a worker abstraction or using Parallel methods, and has well-explained documentation.

https://pub.dev/packages/easy_isolate

The use is simple as

void main() async {
  final worker = Worker();
  await worker.init(mainHandler, isolateHandler);
  
  worker.sendMessage(null);
}

void mainHandler(dynamic data, SendPort isolateSendPort) {
  isolateSendPort.send(null);
}

// Top-level function (or static)
void isolateHandler(dynamic data, SendPort mainSendPort, SendErrorFunction onSendError) {
  mainSendPort.send(null);
}

Or using the Parallel methods

Future main() async {
  await Parallel.foreach(['test'], writeFile);
}

// Top-level function (or static)
void writeFile(String name) {
  File(Directory.systemTemp.path + '/$name').createSync();
}
Lucas Britto
  • 355
  • 1
  • 8