Search code examples
flutterdartdart-isolatesflutter-downloader

dart Isolate listen don't triggered (or work)


My class:

import 'dart:isolate';
import 'dart:ui';

import 'package:flutter_downloader/flutter_downloader.dart';

class SettingsPage extends StatefulWidget with WidgetsBindingObserver {
  SettingsPage({Key? key}) : super(key: key);

  @override
  State<SettingsPage> createState() => _SettingsPageState();
}

class _SettingsPageState extends State<SettingsPage> {
  final ReceivePort _port = ReceivePort();
  int uploadProgress = 0;

  // (-1)
  DownloadTaskStatus uploadStatus = DownloadTaskStatus(-1);
  String identifier = '';

  get downloadsPath =>
      storage.read('downloadsPath') ?? AndroidPathProvider.downloadsPath;

  @override
  void initState() {
    super.initState();

    _bindBackgroundIsolate();

    FlutterDownloader.registerCallback(downloadCallback);
  }
 
  @override
  void dispose() {
    _unbindBackgroundIsolate();
    super.dispose();
  }

  void _bindBackgroundIsolate() {
    final isSuccess = IsolateNameServer.registerPortWithName(
        _port.sendPort, 'downloader_send_port');
    simplelog.i('regPort: $isSuccess');

    if (!isSuccess) {
      _unbindBackgroundIsolate();
      _bindBackgroundIsolate();
      return;
    }

    ///Listening for the data is comming other isolataes
    simplelog.i('before listen');

    _port.listen((dynamic data) {
      simplelog.i('in listen');
      final String _id = data[0];
      final DownloadTaskStatus _status = data[1];
      final int _progress = data[2];

      simplelog.i(
        'Callback on UI isolate: '
        'task ($_id) is in status ($_status) and process ($_progress)',
      );

      setState(() {
        uploadProgress = _progress;
        uploadStatus = _status;
        simplelog.i('inside setState');
      });

      simplelog.i(
          'uploadProgress = $uploadProgress \n uploadStatus = $uploadStatus \n uploadStatus.value = ${uploadStatus.value}');
      if (uploadProgress == 100 &&
          uploadStatus == DownloadTaskStatus.complete &&
          uploadStatus.value == 3) {
        try {
          OpenFilex.open('$downloadsPath/${Consts.apkName}');
        } catch (e) {
          simplelog.e('Cannot open file ${Consts.apkName}! \n $e');
        }
      }
    }, onError: (err) {
      simplelog.e('ISOLATE LISTEN ERROR: $err');
    },
        onDone: () {
      simplelog.i('onDone listen');
    });
  }

  void _unbindBackgroundIsolate() {
    IsolateNameServer.removePortNameMapping('downloader_send_port');
  }

  @pragma('vm:entry-point')
  static void downloadCallback(
      String id,
      DownloadTaskStatus status,
      int progress,
      ) {
    simplelog.i('Callback on background isolate: '
        'task ($id) is in status ($status) and progress ($progress)');

    IsolateNameServer.lookupPortByName('downloader_send_port')
        ?.send([id, status, progress]);
  }

In case with dowloadCallback all work good, but in case _port.listen(), I don't get any data (simplelog = print, it nothing to show me, work only case simplelog.i('before listen');

In init() method register callback, this prints works good, and file success downloaded

How I can solve this problem?

Tried to play around with opening and closing ports using Compiler


Solution

  • This problem came with an inappropriate type parameter sent by send(...).

    Step 1: Change status to primitive status.value

    IsolateNameServer.lookupPortByName('downloader_send_port')
            ?.send([id, status.value, progress]);
    

    Step 2: Convert the int back to a DownloadTaskStatus after receiving it.

       _port.listen((dynamic data) {
          ···
          final DownloadTaskStatus _status = DownloadTaskStatus(data[1] as int);
          ···
       }
    

    You can see more detailed examples from the author's GitHub repo.