skip to Main Content

In my Project I have a heavy async task which is executed in a secondary Isolate.

I want to display the progress of this task to the UI with a ProgressIndicator().

I tried using a callback function passed to the Isolate as a parameter, but this did not work.

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';

class IsolateProgress extends StatelessWidget {
  const IsolateProgress({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Isolate Progress')),
      body: FutureBuilder(
        future: compute(_heavyTask, [1, 11, 111, 1111, 11111, 96953, 111111, 99971, 11111111, 999999487]),
        builder: (context, AsyncSnapshot<Map<int, bool>> snapshot) {
          if (snapshot.hasData) {
            return ListView(
              children: snapshot.data!.entries.map((e) => Text('${e.key}: ${e.value ? 'prime' : 'no prime'}')).toList(),
            );
          }
          return Center(
            child: CircularProgressIndicator(
                // value: x (from Isolate),
                ),
          );
        },
      ),
    );
  }

  Future<Map<int, bool>> _heavyTask(List<int> listToCheck) async {
    Map<int, bool> result = {};
    for (int num in listToCheck) {
      // async call on each loop
      await Future.delayed(Duration(milliseconds: 100));
      if (num == 1) {
        result[1] = false;
      }
      result[num] = true;
      for (int i = 2; i < num; ++i) {
        if (num % i == 0) {
          result[num] = false;
        }
      }
      // here: somehow send a message to main Isolate to add a step to CircularProgressIndicator()
    }
    return result;
  }
}

2

Answers


  1. I’m working on how to return Stream from Isolate.
    But is this what you need?(Async version)

    import 'package:flutter/material.dart';
    
    class HeavyTaskData{
      HeavyTaskData({required this.progress,this.result});
      final double progress;
      final Map<int, bool>? result;
    }
    
    class IsolateProgress extends StatelessWidget {
      const IsolateProgress({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text('Isolate Progress')),
          body: StreamBuilder<HeavyTaskData>(
            stream:_heavyTask([1, 11, 111, 1111, 11111, 96953, 111111, 99971, 11111111, 999999487]),
            builder: (context, AsyncSnapshot<HeavyTaskData> snapshot) {
              if (snapshot.hasData) {
                if(snapshot.data!.result!=null){
                  return ListView(
                    children: snapshot.data!.result!.entries.map((e) => Text('${e.key}: ${e.value ? 'prime' : 'no prime'}')).toList(),
                  );
                }
              }
              return Center(
                child: CircularProgressIndicator(
                  value: snapshot.data?.progress??0,
                  // value: x (from Isolate),
                ),
              );
            },
          ),
        );
      }
    
      Stream<HeavyTaskData> _heavyTask(List<int> listToCheck) async*{
        Map<int, bool> result = {};
        for (int num in listToCheck) {
          // async call on each loop
          await Future.delayed(const Duration(milliseconds: 100));
          if (num == 1) {
            result[1] = false;
          }
          result[num] = true;
          for (int i = 2; i < num; ++i) {
            if (num % i == 0) {
              result[num] = false;
            }
          }
          // here: somehow send a message to main Isolate to add a step to CircularProgressIndicator()
          yield HeavyTaskData(progress: result.length/listToCheck.length);
        }
         yield HeavyTaskData(progress: result.length/listToCheck.length,result:result ) ;
      }
    }
    
    Login or Signup to reply.
  2. (Isolate version)
    You need to create ReceivePort and sendPort to communicate between different Isolate.

    import 'dart:isolate';
    
    import 'package:flutter/material.dart';
    
    class HeavyTaskData {
      HeavyTaskData({required this.progress, this.result});
    
      double progress;
      Map<int, bool>? result;
    
      @override
      String toString() {
        return '{progress:$progress, result:$result}';
      }
    }
    
    class IsolateProgress extends StatelessWidget {
      const IsolateProgress({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text('Isolate Progress')),
          body: StreamBuilder<HeavyTaskData>(
            stream: _heavyTaskStreamReceiver([
              1,
              11,
              111,
              1111,
              11111,
              96953,
              111111,
              99971,
              11111111,
              999999487
            ]),
            builder: (context, AsyncSnapshot<HeavyTaskData> snapshot) {
              if (snapshot.hasData) {
                if (snapshot.data!.result != null && snapshot.data!.progress == 1) {
                  return ListView(
                    children: snapshot.data!.result!.entries
                        .map((e) =>
                            Text('${e.key}: ${e.value ? 'prime' : 'no prime'}'))
                        .toList(),
                  );
                }
              }
              return Center(
                child: CircularProgressIndicator(
                  value: snapshot.data?.progress ?? 0,
                  // value: x (from Isolate),
                ),
              );
            },
          ),
        );
      }
    
      void heavyTask(List<dynamic> args)async{
        Map<int, bool> result = {};
        SendPort sendPort = args[0];
        List<int> listToCheck = args[1];
    
        double progress = 0;
        for (int num in listToCheck) {
          await Future.delayed(const Duration(milliseconds: 100));
          if (num == 1) {
            result[1] = false;
          }
          result[num] = true;
          for (int i = 2; i < num; ++i) {
            if (num % i == 0) {
              result[num] = false;
            }
          }
          progress = result.length/listToCheck.length;
          sendPort.send(HeavyTaskData(progress: progress,result:result));
        }
        sendPort.send(HeavyTaskData(progress: progress,result:result));
      }
    
      Stream<HeavyTaskData> _heavyTaskStreamReceiver(List<int> listToCheck) async* {
        final receivePort = ReceivePort();
        Isolate.spawn(heavyTask,[receivePort.sendPort,listToCheck]);
        await for (var message in receivePort){
          if(message is HeavyTaskData){
            yield message;
            if(message.progress >= 1){
              receivePort.close();
              return;
            }
          }
        }
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search