flutterdartbackground-processdart-isolates

Flutter Isolate.run with multiple arguments


I want to implement something like, when I tap on a download button, it will download the data from the API and store it in the database. Currently this code is working fine, though I haven't added any error handlers,

Suddenly, I noticed about Isolate.run function, which does a lot of work in the back.

I want to replace this workable code with the Isolate.run

But I have no idea about how to do it.

Is this code good enough? If not, then which things I need to implement here?

I've a class Named, MyIsolate, which is look like something,

class MyIsolate {
  @pragma('vm:entry-point')
  static Future<void> start(void Function(List<Object>) entryPoint,
      RootIsolateToken token, int randomId, SendPort uiSendport) async {
    final receivePort = ReceivePort();
    final isolate = await Isolate.spawn(
      entryPoint,
      [receivePort.sendPort, token, randomId],
    );

    receivePort.listen((message) {
      print('message $message');
      if (message == 'done') {
        print('done');
        receivePort.close();
        uiSendport.send('done');
        isolate.kill();
      }
    });
  }
}

From the UI, I'm using this like,

  FloatingActionButton(
            onPressed: () async {
              //randomID between 1 and 10
              final apiID = 1 + Random().nextInt(10 - 1);
              receivePort = ReceivePort();

              MyIsolate.start(
                isolateEntryPoint,
                RootIsolateToken.instance!,
                apiID,
                receivePort.sendPort,
              );
              //receivePort.asBroadcastStream()
              receivePort.asBroadcastStream().listen((message) {
                print('message in ui $message');
                if (message == 'done') {
                  //show snackbar
                  receivePort.close();
                }
              });
            },
            tooltip: 'Download Background',
            child: const Icon(Icons.download),
          )

And this is my code that executes inside the Isolate,

void isolateEntryPoint(List<Object> args) async {
  final SendPort sendPort = args[0] as SendPort;
  final RootIsolateToken token = args[1] as RootIsolateToken;
  final apiID = args[2] as int;

  BackgroundIsolateBinaryMessenger.ensureInitialized(token);

  // Make the network call with Dio
  Dio dio = Dio();
  Response response =
      await dio.get('https://jsonplaceholder.typicode.com/users/$apiID');

  DriftIsolate isolate;

  // Parse the API response into Dart objects
  final parsedData = UserDto.fromJson(response.data);
  print(parsedData.toString());

  DatabaseConnection connection = DatabaseConnection.delayed(() async {
    isolate = await backgroundConnectionIsolate();
    return await isolate.connect();
  }());

  MainDatabase db = MainDatabase.connect(connection);
  UsersDAO dao = UsersDAO(db);
  final response1 = await dao.insertUser(parsedData);
  print(response1);
  // Send the parsed data back to the main isolate
  if (response1) {
    sendPort.send(parsedData.toString());
  } else {
    sendPort.send(null);
  }
  //close
  await connection.executor.close();
  db.close();
  sendPort.send('done');
}

I've tested with Isolate.run(), but I can't understand how to pass the arguments, as it throws an error.

I also searched in ChatGPT. Maybe I'm not telling the ChatGPT in the proper way, how to do it.


Solution

  • In Isolate.spawn(), you pass arguments directly to the entry point. In Isolate.run(), you need to use closures to pass arguments. You encapsulate your function and its arguments within a closure and pass the closure to Isolate.run().

    Modify the entry point for Isolate.run() by creating a closure that includes your original isolateEntryPoint function and its arguments. Ensure that your closure correctly handles the necessary operations and communication with the main isolate.

    // Closure that includes your isolate entry point and its arguments
    void Function() createIsolateFunction(SendPort sendPort, RootIsolateToken 
     token, int apiID) {
      return () {
        isolateEntryPoint(sendPort, token, apiID);
       };
     }
    
     // Modified start method to use Isolate.run
        static Future<void> startWithIsolateRun(
     SendPort uiSendPort, RootIsolateToken token, int randomId) async {
    final receivePort = ReceivePort();
    final closure = createIsolateFunction(receivePort.sendPort, token, 
      randomId);
    
     await Isolate.run(closure);
    
      receivePort.listen((message) {
        // Handle messages and potentially kill the isolate
      });
    }
    
    // Usage
    MyIsolate.startWithIsolateRun(receivePort.sendPort, 
    RootIsolateToken.instance!, apiID);
    

    In this example, createIsolateFunction creates a closure that includes your original entry point and its arguments. This closure is then executed in a new isolate by Isolate.run().