flutter_downloader icon indicating copy to clipboard operation
flutter_downloader copied to clipboard

registerCallback not working in flutter 3.29 and flutter_downloader 1.12.0

Open pachecotatih opened this issue 10 months ago • 15 comments

I initialize on main.dart the plugin. In a streamstore I create the receivePort and IsolateNameServer. After that add the listen of port and add registercallback above. In 1.11.8 was working, in 1.12.0 the callback is not working.

pachecotatih avatar Feb 25 '25 19:02 pachecotatih

Hey @pachecotatih I also faced the same error did you find the solution?

kingkarki avatar Feb 27 '25 09:02 kingkarki

I reinstall to flutter 3.24 (older version) and I used to flutter_downloader 1.11.8... about this, nothing solution

pachecotatih avatar Feb 27 '25 11:02 pachecotatih

And now, in 1.11.8 version and flutter 3.24, I have the problem with initialize download, and when I close the app, the download is canceled. I'm testing on my device, it's used to android 14

pachecotatih avatar Feb 27 '25 12:02 pachecotatih

Yes same issue. I had to update the flutter version because AppConnect required to upload apps with SDK 18 after April 24. So updated to the latest Flutter version 3.29. Everything else works fine, but this package does not work like it supposed to. I downloaded the example code in the project, and the same issue. When I click on download file, the callback does not work and gives me this error:

E/libEGL ( 4649): called unimplemented OpenGL ES API D/EGL_emulation( 4649): app_time_stats: avg=284.93ms min=3.45ms max=10068.07ms count=36 D/DownloadWorker( 4649): DownloadWorker{url=http://enos.itcollege.ee/~jpoial/allalaadimised/reading/Android-Programming-Cookbook.pdf,filename=null,savedDir=/data/user/0/vn.hunghd.example/app_flutter,header={"auth":"test_for_sql_encoding"},isResume=false,status=ENQUEUED E/DartVM ( 4649): ERROR: To access 'package:flutter_downloader_example/home_page.dart::_MyHomePageState' from native code, it must be annotated. E/DartVM ( 4649): ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md E/flutter ( 4649): [ERROR:flutter/lib/ui/dart_runtime_hooks.cc(38)] Dart Error: ERROR: To access 'package:flutter_downloader_example/home_page.dart::_MyHomePageState' from native code, it must be annotated. E/flutter ( 4649): ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md D/DownloadWorker( 4649): Update notification: {notificationId: 6, title: http://enos.itcollege.ee/~jpoial/allalaadimised/reading/Android-Programming-Cookbook.pdf, status: RUNNING, progress: 0} E/flutter ( 4649): [ERROR:flutter/shell/common/shell.cc(115)] Dart Error: ERROR: To access 'package:flutter_downloader_example/home_page.dart::_MyHomePageState' from native code, it must be annotated. E/flutter ( 4649): ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md E/flutter ( 4649): [ERROR:flutter/shell/common/shell.cc(115)] Dart Error: ERROR: To access 'package:flutter_downloader_example/home_page.dart::_MyHomePageState' from native code, it must be annotated. E/flutter ( 4649): ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md D/DownloadWorker( 4649): Open connection to http://enos.itcollege.ee/~jpoial/allalaadimised/reading/Android-Programming-Cookbook.pdf D/DownloadWorker( 4649): Headers = {"auth":"test_for_sql_encoding"} D/TrafficStats( 4649): tagSocket(5) with statsTag=0xffffffff, statsUid=-1 D/DownloadWorker( 4649): Response with redirection code D/DownloadWorker( 4649): Location = https://enos.itcollege.ee/~jpoial/allalaadimised/reading/Android-Programming-Cookbook.pdf D/DownloadWorker( 4649): New url: https://enos.itcollege.ee/~jpoial/allalaadimised/reading/Android-Programming-Cookbook.pdf D/DownloadWorker( 4649): Open connection to https://enos.itcollege.ee/~jpoial/allalaadimised/reading/Android-Programming-Cookbook.pdf D/DownloadWorker( 4649): Headers = {"auth":"test_for_sql_encoding"} D/TrafficStats( 4649): tagSocket(150) with statsTag=0xffffffff, statsUid=-1 I/trustAllHosts( 4649): checkServerTrusted D/DownloadWorker( 4649): Content-Type = application/pdf D/DownloadWorker( 4649): Content-Length = 8620159 D/DownloadWorker( 4649): Charset = null D/DownloadWorker( 4649): Content-Disposition = null D/DownloadWorker( 4649): fileName = Android-Programming-Cookbook.pdf E/DownloadWorker( 4649): It looks like you are trying to save file in public storage but not setting 'saveInPublicStorage' to 'true' D/DownloadWorker( 4649): Update too frequently!!!!, but it is the final update, we should sleep a second to ensure the update call can be processed E/DartVM ( 4649): ERROR: To access 'package:flutter_downloader_example/home_page.dart::_MyHomePageState' from native code, it must be annotated. E/DartVM ( 4649): ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md E/flutter ( 4649): [ERROR:flutter/lib/ui/dart_runtime_hooks.cc(38)] Dart Error: ERROR: To access 'package:flutter_downloader_example/home_page.dart::_MyHomePageState' from native code, it must be annotated. E/flutter ( 4649): ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md E/flutter ( 4649): [ERROR:flutter/shell/common/shell.cc(115)] Dart Error: ERROR: To access 'package:flutter_downloader_example/home_page.dart::_MyHomePageState' from native code, it must be annotated. E/flutter ( 4649): ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md E/flutter ( 4649): [ERROR:flutter/shell/common/shell.cc(115)] Dart Error: ERROR: To access 'package:flutter_downloader_example/home_page.dart::_MyHomePageState' from native code, it must be annotated. E/flutter ( 4649): ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md D/DownloadWorker( 4649): Update notification: {notificationId: 6, title: http://enos.itcollege.ee/~jpoial/allalaadimised/reading/Android-Programming-Cookbook.pdf, status: FAILED, progress: -1} W/System.err( 4649): java.lang.NullPointerException W/System.err( 4649): at vn.hunghd.flutterdownloader.DownloadWorker.downloadFile(DownloadWorker.kt:395) W/System.err( 4649): at vn.hunghd.flutterdownloader.DownloadWorker.doWork(DownloadWorker.kt:206) W/System.err( 4649): at androidx.work.Worker$1.run(Worker.java:82) W/System.err( 4649): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) W/System.err( 4649): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644) W/System.err( 4649): at java.lang.Thread.run(Thread.java:1012) I/WM-WorkerWrapper( 4649): Worker result FAILURE for Work [ id=739e23b8-5c26-4e31-b0b4-a7a15605ea74, tags={ vn.hunghd.flutterdownloader.DownloadWorker, flutter_download_task } ]

erdrzz avatar Mar 06 '25 09:03 erdrzz

Ah, the solution is to mark the whole class with the annotation

@pragma('vm:entry-point') // Add the annotation here
class _MyHomePageState extends State<MyHomePage> {
  // ... your existing code ...
}

erdrzz avatar Mar 06 '25 10:03 erdrzz

same issue here

Goodstuff4UonYT avatar Mar 09 '25 08:03 Goodstuff4UonYT

Ah, the solution is to mark the whole class with the annotation

@pragma('vm:entry-point') // Add the annotation here
class _MyHomePageState extends State<MyHomePage> {
  // ... your existing code ...
}

did not work for me

Goodstuff4UonYT avatar Mar 09 '25 08:03 Goodstuff4UonYT

same issue. it is not working with v3.29.0

alexaungmyooo avatar Mar 12 '25 09:03 alexaungmyooo

Try it this way (works for me):

class MyClass extends StatefulWidget { const MyClass({super.key});

@override State<MyClass> createState() => _MyClassState(); }

@pragma("vm:entry-point"). <<<----- ADD IT HERE class _MyClassState extends State<MyClass> {

admincode1 avatar Mar 15 '25 02:03 admincode1

I got this working on Flutter 3.29.2 with flutter_downloader 1.12.0 by adding the provider block in all manifests and the res/xml/provider_paths.xml. The downloader documentation has the provider block, and the xml for provider_paths.xml should look something like this: <?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <root-path name="root" path="." /> <external-path name="external_files" path="." /> </paths>

oscargoldman avatar Mar 19 '25 14:03 oscargoldman

Ah, the solution is to mark the whole class with the annotation

@pragma('vm:entry-point') // Add the annotation here
class _MyHomePageState extends State<MyHomePage> {
  // ... your existing code ...
}

did not work for me

syahrulafrizal avatar Mar 21 '25 05:03 syahrulafrizal

Annotating the class (not just the callback method) worked for me.

Initially, from initiating one download I got these all error messages

ERROR: To access 'package:kencube/download_controller.dart::DownloadController' from native code, it must be annotated.
ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md
[ERROR:flutter/lib/ui/dart_runtime_hooks.cc(38)] Dart Error: ERROR: To access 'package:kencube/download_controller.dart::DownloadController' from native code, it must be annotated.
ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md
[ERROR:flutter/shell/common/shell.cc(115)] Dart Error: ERROR: To access 'package:kencube/download_controller.dart::DownloadController' from native code, it must be annotated.
ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md
[ERROR:flutter/shell/common/shell.cc(115)] Dart Error: ERROR: To access 'package:kencube/download_controller.dart::DownloadController' from native code, it must be annotated.
ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md
ERROR: To access 'package:kencube/download_controller.dart::DownloadController' from native code, it must be annotated.
ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md
[ERROR:flutter/lib/ui/dart_runtime_hooks.cc(38)] Dart Error: ERROR: To access 'package:kencube/download_controller.dart::DownloadController' from native code, it must be annotated.
ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md
[ERROR:flutter/shell/common/shell.cc(115)] Dart Error: ERROR: To access 'package:kencube/download_controller.dart::DownloadController' from native code, it must be annotated.
ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md
[ERROR:flutter/shell/common/shell.cc(115)] Dart Error: ERROR: To access 'package:kencube/download_controller.dart::DownloadController' from native code, it must be annotated.
ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/aot/entry_point_pragma.md

I did have the callback annotated with @pragma('vm:entry-point') - and it worked nicely before the Flutter upgrade to 3.29

Only after the upgrade I started to see these error messages.

The odd thing was that these errors also appeared when I did not register the callback (when I remarked the FlutterDownloader.registerCallback()!

I do not not understand what in my DownloadController FlutterDownloader was calling then and why, but it cannot have been the callback...

But in any case, once I added the annotation to the class itself, all was back to normal again and it worked without error messages.

@pragma('vm:entry-point') // Make sure optimizer does not think this is unused!
class DownloadController extends GetxController {
...

So bottom line: It fixed the issue for me.

jdeepwell avatar Mar 28 '25 12:03 jdeepwell

I had to add the annotation to the class and the callback function. Adding it only to the class didn't help in my case.

ChristopherKL avatar Apr 03 '25 14:04 ChristopherKL

Yes, sorry for the unprecise wording. I also in the end had it on both, the callback and the class.

jdeepwell avatar Apr 03 '25 14:04 jdeepwell

@pragma('vm:entry-point') worked for

here is usage

`import 'dart:async'; import 'dart:io'; import 'dart:isolate'; import 'dart:ui'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart';

@pragma('vm:entry-point') class AudioDownloadService { // Stream controller for download progress final StreamController<DownloadProgress> _downloadProgressController = StreamController<DownloadProgress>.broadcast();

Stream<DownloadProgress> get downloadProgressStream => _downloadProgressController.stream;

// Port for receiving download updates final ReceivePort _port = ReceivePort();

// Map to track download tasks final Map<String, String> _downloadTasks = {};

AudioDownloadService() { _init(); }

void _init() { // Register callback for download updates IsolateNameServer.registerPortWithName( _port.sendPort, 'downloader_send_port', );

_port.listen((dynamic data) {
  final String id = data[0];
  final int status = data[1];
  final int progress = data[2];

  _handleDownloadUpdate(id, status, progress);
});

// Set up the callback
FlutterDownloader.registerCallback(downloadCallback);

}

// Static callback function for download updates @pragma('vm:entry-point') static void downloadCallback(String id, int status, int progress) { final SendPort? send = IsolateNameServer.lookupPortByName( 'downloader_send_port', ); send?.send([id, status, progress]); }

void _handleDownloadUpdate(String taskId, int status, int progress) { final downloadStatus = _mapDownloadStatus(status);

_downloadProgressController.add(
  DownloadProgress(
    received: progress,
    total: 100,
    percentage: progress.toDouble(),
    speed: 0.0, // flutter_downloader doesn't provide speed
    status: downloadStatus,
    taskId: taskId,
  ),
);

}

DownloadStatus _mapDownloadStatus(int status) { // Based on flutter_downloader status codes: // 0: undefined, 1: enqueued, 2: running, 3: complete, 4: failed, 5: canceled, 6: paused switch (status) { case 2: // running return DownloadStatus.downloading; case 3: // complete return DownloadStatus.completed; case 4: // failed return DownloadStatus.failed; case 5: // canceled return DownloadStatus.failed; case 6: // paused return DownloadStatus.downloading; default: return DownloadStatus.idle; } }

Future<String?> downloadMp3(String url, {String? fileName}) async { try { // Request permissions await _requestPermissions();

  // Get download directory - using internal storage only
  Directory directory = await getApplicationDocumentsDirectory();
  final downloadPath = '${directory.path}/downloads';

  // Create directory if it doesn't exist
  await Directory(downloadPath).create(recursive: true);

  // Generate filename
  fileName ??= 'audio_${DateTime.now().millisecondsSinceEpoch}.mp3';

  // Initialize progress
  _downloadProgressController.add(
    DownloadProgress(
      received: 0,
      total: 100,
      percentage: 0.0,
      speed: 0.0,
      status: DownloadStatus.downloading,
    ),
  );

  // Start download
  final taskId = await FlutterDownloader.enqueue(
    url: url,
    savedDir: downloadPath,
    fileName: fileName,
    headers: {}, // Optional headers
    showNotification: false, // Disable notifications to avoid icon issues
    openFileFromNotification: false,
    saveInPublicStorage: false, // Set to false to avoid permission issues
  );

  if (taskId != null) {
    _downloadTasks[taskId] = fileName;

    // Wait for download completion
    await _waitForDownloadCompletion(taskId);

    final filePath = '$downloadPath/$fileName';
    return filePath;
  }

  return null;
} catch (e) {
  print('Download error: $e');
  _downloadProgressController.add(
    DownloadProgress(
      received: 0,
      total: 100,
      percentage: 0.0,
      speed: 0.0,
      status: DownloadStatus.failed,
    ),
  );
  return null;
}

}

Future _waitForDownloadCompletion(String taskId) async { final completer = Completer();

late StreamSubscription subscription;
subscription = downloadProgressStream.listen((progress) {
  if (progress.taskId == taskId) {
    if (progress.status == DownloadStatus.completed ||
        progress.status == DownloadStatus.failed) {
      subscription.cancel();
      completer.complete();
    }
  }
});

return completer.future;

}

Future pauseDownload(String taskId) async { await FlutterDownloader.pause(taskId: taskId); }

Future resumeDownload(String taskId) async { await FlutterDownloader.resume(taskId: taskId); }

Future cancelDownload(String taskId) async { await FlutterDownloader.cancel(taskId: taskId); }

Future retryDownload(String taskId) async { await FlutterDownloader.retry(taskId: taskId); }

Future<List<DownloadTask>?> getDownloadTasks() async { return await FlutterDownloader.loadTasks(); }

void dispose() { _downloadProgressController.close(); _port.close(); IsolateNameServer.removePortNameMapping('downloader_send_port'); }

Future _requestPermissions() async { if (Platform.isAndroid) { // For Android 13 and above if (await Permission.notification.isDenied) { await Permission.notification.request(); }

  // For Android 10 and below
  if (await Permission.storage.isDenied) {
    await Permission.storage.request();
  }

  // For Android 11 and above
  if (await Permission.manageExternalStorage.isDenied) {
    await Permission.manageExternalStorage.request();
  }
}

} }

// Download progress model class DownloadProgress { final int received; final int total; final double percentage; final double speed; // bytes per second final DownloadStatus status; final String? taskId;

DownloadProgress({ required this.received, required this.total, required this.percentage, required this.speed, required this.status, this.taskId, });

String get formattedSpeed { if (speed < 1024) { return '${speed.toStringAsFixed(1)} B/s'; } else if (speed < 1024 * 1024) { return '${(speed / 1024).toStringAsFixed(1)} KB/s'; } else { return '${(speed / (1024 * 1024)).toStringAsFixed(1)} MB/s'; } }

String get formattedSize { final totalMB = total / (1024 * 1024); final receivedMB = received / (1024 * 1024); return '${receivedMB.toStringAsFixed(1)} MB / ${totalMB.toStringAsFixed(1)} MB'; } }

enum DownloadStatus { downloading, completed, failed, idle } `

here is ui

import 'package:audio_background_download/service/audio_download_service.dart'; import 'package:flutter/material.dart';

class AudioDownloadScreen extends StatefulWidget { const AudioDownloadScreen({super.key});

@override State<AudioDownloadScreen> createState() => _AudioDownloadScreenState(); }

@pragma('vm:entry-point') class _AudioDownloadScreenState extends State<AudioDownloadScreen> { final String _audioUrl = "https://github.com/rafaelreis-hotmart/Audio-Sample-files/raw/master/sample.mp3"; bool _isDownloading = false;

final AudioDownloadService audioService = AudioDownloadService();

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('MP3 Background Downloader'), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Card( elevation: 4, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Audio URL:', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), const SizedBox(height: 8), Text( _audioUrl, style: const TextStyle(fontSize: 12, color: Colors.grey), ), ], ), ), ), const SizedBox(height: 20),

        // Download Progress Section
        StreamBuilder<DownloadProgress>(
          stream: audioService.downloadProgressStream,
          builder: (context, snapshot) {
            final progress = snapshot.data;

            if (progress != null &&
                progress.status == DownloadStatus.downloading) {
              return Card(
                elevation: 4,
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          const Text(
                            'Download Progress',
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 16,
                            ),
                          ),
                          Text(
                            '${progress.percentage.toStringAsFixed(1)}%',
                            style: const TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 16,
                              color: Colors.blue,
                            ),
                          ),
                        ],
                      ),
                      const SizedBox(height: 10),
                      LinearProgressIndicator(
                        value: progress.percentage / 100,
                        backgroundColor: Colors.grey[300],
                        valueColor: const AlwaysStoppedAnimation<Color>(
                          Colors.blue,
                        ),
                        minHeight: 8,
                      ),
                      const SizedBox(height: 10),
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Text(
                            progress.formattedSize,
                            style: const TextStyle(
                              fontSize: 12,
                              color: Colors.grey,
                            ),
                          ),
                          Text(
                            progress.formattedSpeed,
                            style: const TextStyle(
                              fontSize: 12,
                              color: Colors.grey,
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              );
            }

            return const SizedBox.shrink();
          },
        ),

        const SizedBox(height: 20),

        // Download Button
        ElevatedButton.icon(
          onPressed: _isDownloading ? null : _downloadAudio,
          icon:
              _isDownloading
                  ? const SizedBox(
                    width: 20,
                    height: 20,
                    child: CircularProgressIndicator(
                      strokeWidth: 2,
                      color: Colors.white,
                    ),
                  )
                  : const Icon(Icons.download),
          label: Text(_isDownloading ? 'Downloading...' : 'Download MP3'),
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.blue,
            foregroundColor: Colors.white,
            padding: const EdgeInsets.symmetric(vertical: 12),
          ),
        ),

        const SizedBox(height: 10),
      ],
    ),
  ),
);

}

Future _downloadAudio() async { setState(() { _isDownloading = true; });

try {
  final filePath = await audioService.downloadMp3(
    _audioUrl,
    fileName: 'test_audio.mp3',
  );

  if (filePath != null) {
    setState(() {});

    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Row(
          children: [
            Icon(Icons.check_circle, color: Colors.white),
            SizedBox(width: 8),
            Text('Download completed successfully!'),
          ],
        ),
        backgroundColor: Colors.green,
      ),
    );
  } else {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Row(
          children: [
            Icon(Icons.error, color: Colors.white),
            SizedBox(width: 8),
            Text('Download failed!'),
          ],
        ),
        backgroundColor: Colors.red,
      ),
    );
  }
} catch (e) {
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      content: Row(
        children: [
          const Icon(Icons.error, color: Colors.white),
          const SizedBox(width: 8),
          Expanded(child: Text('Error: $e')),
        ],
      ),
      backgroundColor: Colors.red,
    ),
  );
} finally {
  setState(() {
    _isDownloading = false;
  });
}

} }

thelastknightahk avatar Jul 04 '25 23:07 thelastknightahk