Kareem Mohamed
Kareem Mohamed

Reputation: 61

flutter_downloader get progress for many tasks

I'm working on Download Manager app using Flutter_downloader package, it's the only package that gives us a lot of options such as pause, resume, retry, cancel, remove and more, but the problem I'm facing is a progress problem.

I'm using a download model file to define a download

import 'package:flutter/material.dart';

class Download {
  final String name;
  final String size;
  final String status;
  final String timeLeft;
  final String transferRate;
  final String type;
  final String path;
  final String icon;
  int progress;

  Download({
    @required this.name,
    @required this.size,
    @required this.status,
    @required this.timeLeft,
    @required this.transferRate,
    @required this.type,
    @required this.path,
    @required this.icon,
    @required this.progress,
  });
}

and downloads provider file to get all downloads and handle all download actions

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

import 'package:idminternetdownloadmanager/models/download.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:http/http.dart' as http;
import 'package:ext_storage/ext_storage.dart';

class DownloadsProvider with ChangeNotifier {
  List<Download> _downloads = [];

  List<Download> get downloads {
    return [..._downloads];
  }

  void addDownload(String url) async {
    var taskId;
    //set name
    final name = url.split('/').last;
    //set type
    final type = url.split('.').last;
    //set icon
    final icon = type == 'pdf'
        ? 'lib/assets/pdf.png'
        : type == 'txt'
            ? 'lib/assets/txt.png'
            : type == 'docx' || type == 'doc'
                ? 'lib/assets/doc.png'
                : type == 'zip' || type == 'rar'
                    ? 'lib/assets/rar.png'
                    : type == 'iso'
                        ? 'lib/assets/iso.png'
                        : type == 'tif' ||
                                type == 'jpg' ||
                                type == 'gif' ||
                                type == 'png' ||
                                type == 'raw'
                            ? 'lib/assets/png.png'
                            : type == 'flv' ||
                                    type == 'avi' ||
                                    type == 'mov' ||
                                    type == 'mpeg' ||
                                    type == 'mp4' ||
                                    type == 'ogg' ||
                                    type == 'wmv' ||
                                    type == 'webm' ||
                                    type == '3gp'
                                ? 'lib/assets/mp4.png'
                                : type == 'mp3' ||
                                        type == 'wma' ||
                                        type == 'mid' ||
                                        type == 'wav'
                                    ? 'lib/assets/mp3.png'
                                    : type == 'java'
                                        ? 'lib/assets/java.png'
                                        : type == 'rss'
                                            ? 'lib/assets/rss.png'
                                            : type == 'php'
                                                ? 'lib/assets/php.png'
                                                : type == 'xml'
                                                    ? 'lib/assets/xml.png'
                                                    : type == 'html'
                                                        ? 'lib/assets/html.png'
                                                        : type == 'css'
                                                            ? 'lib/assets/css.png'
                                                            : 'lib/assets/unknown.png';
    //set size
    String size;
    http.Response r = await http.head(url);
    final fileSize = int.parse(r.headers["content-length"]);
    if (fileSize >= 1024 && fileSize < 1048576) {
      size = '${(fileSize / 1024).toStringAsFixed(2)} KB';
    } else if (fileSize >= 1048576 && fileSize < 1073741824) {
      size = '${(fileSize / 1048576).toStringAsFixed(2)} MB';
    } else {
      size = '${(fileSize / 1073741824).toStringAsFixed(2)} G';
    }
    //set downloads directory path
    String path = await ExtStorage.getExternalStoragePublicDirectory(
        ExtStorage.DIRECTORY_DOWNLOADS);
    //start download
    final storagePermission = await Permission.storage.request();
    if (storagePermission.isGranted) {
      taskId = await FlutterDownloader.enqueue(
        url: url,
        savedDir: path,
        showNotification:
            true, // show download progress in status bar (for Android)
        openFileFromNotification:
            true, // click on notification to open downloaded file (for Android)
      );
    } else {
      return;
    }
    
    
    // trying a different way to get progress
    try {
      final tasks = await FlutterDownloader.loadTasksWithRawQuery(
          query: 'SELECT * FROM task WHERE status=3 AND progress<>0');
      final task = tasks.firstWhere((task) => task.taskId == taskId);
      int progress = task.progress;
      print(progress);
    } catch (e) {
      print(e);
    } // and it doesn't work
    
    
    _downloads.add(Download(
      name: name,
      size: size,
      status: 'Unknown',
      timeLeft: 'Unknown',
      transferRate: 'Unknown',
      type: type,
      path: path,
      icon: icon,
      progress: 5555,
    ));
    notifyListeners();
  }
}

and this is a photo of what I'm building // StackOverFlow doesn't want me to upload a photo

and this my ui

@override
  Widget build(BuildContext context) {
    final downloadList = Provider.of<DownloadsProvider>(context).downloads;
    return GestureDetector(
      onTapDown: storePosition,
      onLongPress: () {
        setState(() {
          rowColor = Colors.blue;
        });
        showPopupMenu();
      },
      //showPopupMenu,
      child: Container(
        //height: 25,
        decoration: BoxDecoration(
          color: rowColor,
          border: Border(
            bottom: BorderSide(width: 1, color: Colors.grey[350]),
          ),
        ),
        child: Row(
          children: <Widget>[
            NameRowContainer(
              data: downloadList[widget.index].name,
              icon: downloadList[widget.index].icon,
            ),
            RowContainer(
                data: downloadList[widget.index].size,
                lastOne: false,
                width: 100),
            RowContainer(
              // I want to show progress here
                data: downloadList[widget.index].status,
                lastOne: false,
                width: 100),
            RowContainer(
                data: downloadList[widget.index].timeLeft,
                lastOne: false,
                width: 85),
            RowContainer(
                data: downloadList[widget.index].transferRate,
                lastOne: true,
                width: 120),
          ],
        ),
      ),
    );
  }

so the issue is I can't get progress for each task and store it is a progress variable in download model soo then I can show it to the user in the data table as shown in the photo above and later create a linearprogressindicator based on it the way the package provid to get progress it works for a single file not for all files, or at least I can't work around it any thoughts?

Upvotes: 0

Views: 7355

Answers (3)

IaziBashir
IaziBashir

Reputation: 21

you can use flutter_downloader package . the package performed all its download task on the background isolates, thus enhancing performance and also reducing the tedious work in trying to create yours from scratch. useful post on how using the package: https://medium.com/@ezeaguprincewill5/flutter-downloader-package-implementation-made-easier-84b9153c1fbb

Upvotes: 0

ilia khuzhakhmetov
ilia khuzhakhmetov

Reputation: 101

Use this callback:

static void downloadCallback(
      String id, DownloadTaskStatus status, int progress) {
    final SendPort send =
        IsolateNameServer.lookupPortByName('MyAppPrgrss' + id);
    send.send([id, status, progress]);
  }

And this in your UI part:

void _bindBackgroundIsolate() {
    bool isSuccess = IsolateNameServer.registerPortWithName(
        _port.sendPort, port + _downloadTaskId);
    if (!isSuccess) {
      _unbindBackgroundIsolate();
      _bindBackgroundIsolate();
      return;
    }
    _port.listen((dynamic data) {
      String id = data[0];
      DownloadTaskStatus status = data[1];
      int progress = data[2];

      setState(() {
        if (id == _downloadTaskId) {
          this.progress = progress;

          if (status == DownloadTaskStatus.complete) {
            // TODO
          }
        }
      });
    });
  }

  void _unbindBackgroundIsolate() {
    IsolateNameServer.removePortNameMapping('MyAppPrgrss$_downloadTaskId');
  }

Register callback after start download and initialize taskID

   String _downloadTaskId = await FlutterDownloader.enqueue(
    url: downloadUrl,
    savedDir: path,
    fileName: fileName,
    showNotification: true,
    openFileFromNotification: true,
  );

FlutterDownloader.registerCallback(downloadCallback);

Upvotes: 1

wafL
wafL

Reputation: 655

One of the methods that could be used is making a list of the files' url:

List<String> downloadList= List<String>();
taskId = await FlutterDownloader.enqueue(
    url: url,
    savedDir: path,
    showNotification:
        true, // show download progress in status bar (for Android)
    openFileFromNotification:
        true, // click on notification to open downloaded file (for Android)
  ).then((fileUrl){
    downloadList.add(fileUrl);
  });

Then, iterate through them on the callback funcion in order to get their respective progress.

This query:

final tasks = await FlutterDownloader.loadTasksWithRawQuery(
      query: 'SELECT * FROM task WHERE status=3 AND progress<>0');

Might not be the best solution, since there would be multiple queries every time you wanted to retrieve the value of the progress. Not only is that resource heavy, but possibly inacurate. Callbacks should be the way to go, since they get that same value in a better way, as documented here:

Update download progress: FlutterDownloader.registerCallback(callback); //callback is a top-level or static function

Moreover, this repo has a great example with a lot of details about that same callbacks.

Upvotes: 0

Related Questions