Reputation: 61
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
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
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
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