'I am trying to use flutter Isolate with database data. But it's throwing an error. I don't know why is it happening?

I'm using the hive database. My code is:

HiveStocktaking? stocktaking =
          _database.getStocktakingById(_stocktakingId);
      StocktakingStats stocktakingStats =
          _database.getStocktakingStats(_stocktakingId, true);
      List<HiveStocktakingItem> stocktakingItemShortage = _database
          .getStocktakingAllItem(_stocktakingId, 'shortage', '',
              getAll: withProducts)
          .values
          .first;
      List<HiveStocktakingItem> stocktakingItemSurplus = _database
          .getStocktakingAllItem(_stocktakingId, 'surplus', '',
              getAll: withProducts)
          .values
          .first;

      int shortageLength = withProducts ? stocktakingItemShortage.length : 0;
      int surplusLength = withProducts ? stocktakingItemSurplus.length : 0;

      if (type == 'excel') {
        createIsolate(
          stocktaking,
          stocktakingStats,
          stocktakingItemShortage,
          stocktakingItemSurplus,
          shortageLength,
          surplusLength,
        );
      }

Future createIsolate(
      HiveStocktaking? stocktaking,
      StocktakingStats stocktakingStats,
      List<HiveStocktakingItem> stocktakingItemShortage,
      List<HiveStocktakingItem> stocktakingItemSurplus,
      int shortageLength,
      int surplusLength) async {
    ReceivePort receivePort = ReceivePort();
    Isolate.spawn(isolateFunction, receivePort.sendPort);
    SendPort childSendPort = await receivePort.first;

    ReceivePort responsePort = ReceivePort();
    childSendPort.send([
      stocktaking,
      stocktakingStats,
      stocktakingItemShortage,
      stocktakingItemSurplus,
      shortageLength,
      surplusLength,
      responsePort.sendPort
    ]);

    var sum = await responsePort.first;
    print('sum: $sum');
  }

  void isolateFunction(SendPort mainSendPort) async {
    ReceivePort childReceivePort = ReceivePort();
    mainSendPort.send(childReceivePort.sendPort);

    await for (var message in childReceivePort) {
      HiveStocktaking? stocktaking = message[0];
      StocktakingStats stocktakingStats = message[1];
      List<HiveStocktakingItem> stocktakingItemShortage = message[2];
      List<HiveStocktakingItem> stocktakingItemSurplus = message[3];
      int shortageLength = message[4];
      int surplusLength = message[5];
      SendPort replyPort = message[6];

      //heavy task
      sync.Workbook workbook = sync.Workbook();
      var sheet = workbook.worksheets[0];

      sheet.getRangeByIndex(1, 1)
        ..setText('Отчет по инвентаризации')
        ..columnWidth = 40
        ..cellStyle.bold = true
        ..rowHeight = 30
        ..cellStyle.fontSize = 20;

      sheet.getRangeByIndex(2, 1).setText('Магазин: ${stocktaking?.shopName}');
      sheet
          .getRangeByIndex(3, 1)
          .setText('Дата начала: ${stocktaking?.createdAt}');
      sheet
          .getRangeByIndex(4, 1)
          .setText('Дата завершения: ${stocktaking?.finishedAt}');
      sheet.getRangeByIndex(2, 3)
        ..setText(
            'Отсканировано товаров: ${BaseFunctions.numberRound(stocktakingStats.totalScannedMeasurementValue)}')
        ..columnWidth = 30;
      sheet.getRangeByIndex(3, 3).setText(
          'Недостач: ${BaseFunctions.numberRound(stocktakingStats.totalMeasurementValue)}');
      sheet.getRangeByIndex(4, 3).setText(
          'Излишков: ${BaseFunctions.numberRound(stocktakingStats.surplus)}');

      // etc generating codes
      
      
      List<int> bytes = workbook.saveAsStream();
      workbook.dispose();
      var uint8list = Uint8List.fromList(bytes);
      if (Platform.isMacOS) {
        String fileName = 'stocktaking_report_' +
            DateFormat('dd-MM-yyyy_HH-mm-ss').format(DateTime.now()) +
            '.pdf';
        String? path =
        await PathProviderPlatform.instance.getApplicationSupportPath();
        final File file =
        File(Platform.isWindows ? '$path\\$fileName' : '$path/$fileName');
        await file.writeAsBytes(bytes, flush: true);
        await Process.run('open', <String>['$path/$fileName'], runInShell: true);
      } else {
        await FileSaver.instance.saveFile(
          'stocktaking_report_' +
              DateFormat('dd-MM-yyyy_HH-mm-ss').format(DateTime.now()),
          uint8list,
          'xlsx',
          mimeType: MimeType.MICROSOFTEXCEL,
        );
      }

      replyPort.send(1);
    }
  }

But it is throwing this error:

[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Invalid argument(s): Illegal argument in isolate message: (object extends NativeWrapper - Library:'dart:io' Class: _RandomAccessFileOpsImpl@13069316)

if I give the createIsolate fake data it works perfectly.

if (type == 'excel') {
        ProjectFiles.createIsolate(
          HiveStocktaking(),
          StocktakingStats(),
          [],
          [],
          0,
          0,
        );
      }


Solution 1:[1]

I found a way to solve this issue. If you want to use the hive database in an isolate function note that you must init your database in the isolate function and close the boxes (which you want to use in the isolate) in the main thread. Register the boxes' adapter in the isolate. After your doing close the boxes which opened in isolate. To initiate the database in isolate, you have to set the database path to Hive.init(path). You can send this path through isolate's port. If you want to use closed boxes in the main thread you have to reopen it. Here some codes for example:

static Future<bool> myFunc() async {
   ReceivePort receivePort = ReceivePort();
   Isolate.spawn(isolateFunc, receivePort.sendPort);
   SendPort childSendPort = await receivePort.first;

   await Hive.box<YourBox>('boxName').close();
   // database path
   Directory appDocumentDir = await getApplicationDocumentsDirectory();
   ReceivePort responsePort = ReceivePort();
   childSendPort.send([appDocumentDir, responsePort.sendPort]);
   var sum = await responsePort.first;
   var box = await Hive.openBox<YourBox>('boxName');
   HiveDatabase.setInstanceYourBox(box);
   return sum == 1;
}

static void isolateDownloadStocktaking(SendPort mainSendPort) async {
   ReceivePort childReceivePort = ReceivePort();
   mainSendPort.send(childReceivePort.sendPort);

   await for (var message in childReceivePort) {
     Directory appDocumentDir = message[0];
     Hive.init(appDocumentDir.path);
     Hive.registerAdapter(YourBoxAdapter());
     await HiveDatabase.getInstanceYourBox();
     // you can use your box
     SendPort replyPort = message[1];

     await Hive.box<YourBox>('boxName').close();
     replyPort.send(1);
   }
}

HiveDatabase class is here:

class HiveDatabase {
 static Box<YourBox>? _yourBox;
 static HiveDatabase instance = HiveDatabase._();

 HiveDatabase._();

 static Future<HiveDatabase> getInstanceYourBox() async {
   _yourBox ??= await Hive.openBox<YourBox>('boxName');
   return instance;
 }

 static Future<HiveDatabase> setInstanceYourBox(Box<YourBox> box) async {
   _yourBox = box;
   return instance;
 }

 List<YourBox> elements() {
   return (_yourBox?.values ?? []).toList();
 }

 Future<void> updateElement(YourBox value) async {
   await _yourBox?.put(value.id, value);
 }

 Future<void> addElement(YourBox value) async {
   await _yourBox?.put(value.id, value);
 }

 Future<void> deleteElement(int index) async {
   await _yourBox?.deleteAt(index);
 }

 Future<void> clearYourBox() async {
   await _yourBox?.clear();
 }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Bahrom Jabborov