Search code examples
flutterdart-isolates

Using Isolate to call a function


I am running a test on using isolate to call function, below is my code

import 'dart:isolate';

import 'package:flutter/material.dart';

void main() async {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          final result = await startDownloadUsingRunMethod();
          print('Factorial result: $result');
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }


  Future<String> startDownloadUsingRunMethod() async {
    final imageData = await Isolate.run(_readAndParseJsonWithoutIsolateLogic);
    return imageData;
  }

  Future<String> _readAndParseJsonWithoutIsolateLogic() async {
    await Future.delayed(const Duration(seconds: 2));
    return 'this is downloaded data';
  }
}

everytime when I tap on my floatingActionButton to run the function I will get the error below

E/flutter ( 9611): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument(s): Illegal argument in isolate message: object is unsendable - Library:'dart:async' Class: _AsyncCompleter@4048458 (see restrictions listed at SendPort.send() documentation for more information)

anyone know what cause these issues?


Solution

  • Isolate.run is used for isolates, which run in a separate isolate and do not support async/await operations directly.

    To fix this issue, you can use Isolate.spawn to run a function in a separate isolate and then use a ReceivePort to communicate between the main isolate and the spawned isolate. Here's how you can modify your code to achieve this:

    floatingActionButton: FloatingActionButton(
            onPressed: () async {
              final result = await startDownloadUsingSpawn();
              print('Download result: $result');
            },
            tooltip: 'Download',
            child: const Icon(Icons.download),
          ),
        );
    
    Future<String> startDownloadUsingSpawn() async {
        final receivePort = ReceivePort();
        await Isolate.spawn(_readAndParseJsonWithoutIsolateLogic, receivePort.sendPort);
    
        final completer = Completer<String>();
        receivePort.listen((data) {
          completer.complete(data);
          receivePort.close();
        });
    
        return completer.future;
      }
    
    static void _readAndParseJsonWithoutIsolateLogic(SendPort sendPort) async {
        await Future.delayed(const Duration(seconds: 2));
        final result = 'This is downloaded data';
        sendPort.send(result);
      }