Anonymous
Flutter: загрузка не удалась всегда с использованием flutter_downlaoder
Сообщение
Anonymous » 02 июл 2024, 09:13
Я использую flutter_downloader для загрузки файла по URL-адресу и проверил, что URL-адрес работает нормально и файл загружается, если я загружаю этот URL-адрес в браузере.
То же самое код работает отлично на iOS
Я столкнулся с проблемой только в Android, файл начинает загружаться, но мгновенная загрузка не удалась. Я пытался связаться с GitHub с той же ошибкой, но не получил ответа. некоторые другие разработчики также получают эту ошибку, пожалуйста, проверьте здесь
Я получаю следующие ошибки:
Код: Выделить всё
W/System.err(10971): java.lang.NullPointerException: httpConn.contentType must not be null
W/System.err(10971): at vn.hunghd.flutterdownloader.DownloadWorker.downloadFile(DownloadWorker.kt:342)
W/System.err(10971): at vn.hunghd.flutterdownloader.DownloadWorker.doWork(DownloadWorker.kt:208)
W/System.err(10971): at androidx.work.Worker$1.run(Worker.java:86)
W/System.err(10971): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
W/System.err(10971): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
W/System.err(10971): at java.lang.Thread.run(Thread.java:923)
При инициализации я получаю следующую ошибку:
Код: Выделить всё
E/flutter (11803): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: 'package:flutter_downloader/src/downloader.dart': Failed assertion: line 49 pos 7: '!_initialized': plugin flutter_downloader has already been initialized
E/flutter (11803): #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61)
E/flutter (11803): #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5)
вот мой манифест с разрешениями:
Код: Выделить всё
android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"
android:authorities="im.mingguang.mingguang_app.flutter_downloader.provider"
android:exported="false"
android:grantUriPermissions="true"
android:requestLegacyExternalStorage="true">
Ниже приведен код класса загрузки.
Код: Выделить всё
import 'dart:isolate';
import 'dart:ui';
import 'dart:async';
import 'dart:io';
import 'package:android_path_provider/android_path_provider.dart';
import 'package:device_info/device_info.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:permission_handler/permission_handler.dart';
const debug = true;
List? taskInfoList = [];
late List itemholderList = [];
class DownloadFileScreen extends StatefulWidget with WidgetsBindingObserver {
static const String routeName = "/DownloadFileScreen";
TargetPlatform? platform;
DownloadFileScreen({Key? key}) : super(key: key);
// final String? title;
@override
_DownloadFileScreenState createState() => new _DownloadFileScreenState();
}
class _DownloadFileScreenState extends State {
late bool _isLoading;
late bool _permissionReady;
late String _localPath;
ReceivePort _port = ReceivePort();
@override
void initState() {
super.initState();
_checkPermission();
_bindBackgroundIsolate();
FlutterDownloader.registerCallback(downloadCallback);
_isLoading = true;
_permissionReady = false;
_prepare();
}
@override
void dispose() {
_unbindBackgroundIsolate();
super.dispose();
}
void _bindBackgroundIsolate() async {
await FlutterDownloader.initialize(
debug: true, // optional: set to false to disable printing logs to console (default: true)
ignoreSsl: true // option: set to false to disable working with http links (default: false)
);
bool isSuccess = IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');
if (!isSuccess) {
_unbindBackgroundIsolate();
_bindBackgroundIsolate();
return;
}
_port.listen((dynamic data) async {
if (debug) {
print('UI Isolate Callback: $data');
}
String? id = data[0];
DownloadTaskStatus? status = data[1] == 2
? DownloadTaskStatus.running
: data[1] == 4
? DownloadTaskStatus.failed
: data[1] == 3
? DownloadTaskStatus.complete
: DownloadTaskStatus.undefined;
int? progress = data[2];
if (taskInfoList != null && taskInfoList!.isNotEmpty) {
final task = taskInfoList!.firstWhere((task) => task.taskId == id);
if (status == DownloadTaskStatus.complete) {
await updateAoiRequest(taskInfoList, id);
}
setState(() {
task.status = status;
task.progress = progress;
});
}
});
FlutterDownloader.registerCallback(downloadCallback);
}
Future updateAoiRequest(List? taskInfoList, String? id) async {
// final task = taskInfoList!.firstWhere((task) => task.taskId == id);
// AoiRequest? aoiRequest = await AoiDBHelper.getAoiRequestByDownloadUrl(task.link!);
// if (aoiRequest != null) {
// aoiRequest.downloadStatus = AoiDownloadStatus.Complete;
// AoiRequestBloc.databaseBloc.updateAoiRequest(aoiRequest);
// }
// print(aoiRequest?.fileName);
}
void _unbindBackgroundIsolate() {
IsolateNameServer.removePortNameMapping('downloader_send_port');
}
@pragma('vm:entry-point')
// static void downloadCallback(String id, DownloadTaskStatus status, int progress) {
static void downloadCallback(String id, status, int progress) {
if (debug) {
print('Background Isolate Callback: task ($id) is in status ($status) and process ($progress)');
}
final SendPort send = IsolateNameServer.lookupPortByName('downloader_send_port')!;
send.send([id, status, progress]);
}
@override
Widget build(BuildContext context) {
widget.platform = Theme.of(context).platform;
return Scaffold(
appBar: AppBar(
title: Text("Download list"),
),
body: Builder(
builder: (context) => _isLoading
? Center(
child: CircularProgressIndicator(),
)
: _permissionReady
? _buildDownloadList()
: _buildNoPermissionWarning()),
);
}
Widget _buildDownloadList() => Container(
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 16.0),
children: itemholderList
.map((item) => item.task == null
? _buildListSection(item.name ?? 'Files')
: DownloadItem(
data: item,
onItemClick: (task) {
_openDownloadedFile(task).then((success) {
if (!success) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Cannot open this file')));
}
});
},
onActionClick: (task) {
if (task.status == DownloadTaskStatus.undefined) {
_requestDownload(task);
} else if (task.status == DownloadTaskStatus.running) {
_pauseDownload(task);
} else if (task.status == DownloadTaskStatus.paused) {
_resumeDownload(task);
} else if (task.status == DownloadTaskStatus.complete) {
_delete(task);
} else if (task.status == DownloadTaskStatus.failed) {
_retryDownload(task);
}
},
))
.toList(),
),
);
Widget _buildListSection(String title) => Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Text(
title,
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.blue, fontSize: 18.0),
),
);
Widget _buildNoPermissionWarning() => Container(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Text(
'Please grant accessing storage permission to continue -_-',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.blueGrey, fontSize: 18.0),
),
),
SizedBox(
height: 32.0,
),
TextButton(
onPressed: () {
_retryRequestPermission();
},
child: Text(
'Retry',
style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold, fontSize: 20.0),
))
],
),
),
);
Future _retryRequestPermission() async {
final hasGranted = await _checkPermission();
if (hasGranted) {
await _prepareSaveDir();
}
setState(() {
_permissionReady = hasGranted;
});
}
void _requestDownload(TaskInfo task) async {
print(task.localDownloadPath);
task.taskId = await FlutterDownloader.enqueue(
url: task.link!,
headers: {"content-type": "application/json"},
// headers: {"content-type": "multipart/form-data"},
fileName: task.fileName,
savedDir: task.localDownloadPath!,
showNotification: true,
openFileFromNotification: true,
// saveInPublicStorage: true,
);
}
void _cancelDownload(TaskInfo task) async {
await FlutterDownloader.cancel(taskId: task.taskId!);
}
void _pauseDownload(TaskInfo task) async {
await FlutterDownloader.pause(taskId: task.taskId!);
}
void _resumeDownload(TaskInfo task) async {
String? newTaskId = await FlutterDownloader.resume(taskId: task.taskId!);
task.taskId = newTaskId;
}
void _retryDownload(TaskInfo task) async {
String? newTaskId = await FlutterDownloader.retry(taskId: task.taskId!);
task.taskId = newTaskId;
}
Future _openDownloadedFile(TaskInfo? task) {
if (task != null) {
return FlutterDownloader.open(taskId: task.taskId!);
} else {
return Future.value(false);
}
}
void _delete(TaskInfo task) async {
await FlutterDownloader.remove(taskId: task.taskId!, shouldDeleteContent: true);
await _prepare();
setState(() {});
}
Future _checkPermission() async {
print('check permission called');
if (Platform.isIOS) return true;
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
if (widget.platform == TargetPlatform.android && androidInfo.version.sdkInt
// TaskInfo(name: document['name'], link: document['link'])));
//
// itemholderList.add(ItemHolder(name: 'Documents'));
// for (int i = count; i < taskInfoList!.length; i++) {
// itemholderList.add(ItemHolder(name: taskInfoList![i].name, task: taskInfoList![i]));
// count++;
// }
//
// taskInfoList!.addAll(_images
// .map((image) => TaskInfo(name: image['name'], link: image['link'])));
//
// itemholderList.add(ItemHolder(name: 'Images'));
// for (int i = count; i < taskInfoList!.length; i++) {
// itemholderList.add(ItemHolder(name: taskInfoList![i].name, task: taskInfoList![i]));
// count++;
// }
//
// taskInfoList!.addAll(_videos
// .map((video) => TaskInfo(name: video['name'], link: video['link'])));
//
// itemholderList.add(ItemHolder(name: 'Videos'));
// for (int i = count; i < taskInfoList!.length; i++) {
// itemholderList.add(ItemHolder(name: taskInfoList![i].name, task: taskInfoList![i]));
// count++;
// }
tasks!.forEach((task) async {
for (TaskInfo info in taskInfoList!) {
if (info.link == task.url) {
info.taskId = task.taskId;
info.status = task.status;
info.progress = task.progress;
if (task.status == DownloadTaskStatus.complete) {
await updateAoiRequest(taskInfoList, task.taskId);
} else if (task.status == DownloadTaskStatus.undefined) {
_requestDownload(info);
}
}
}
});
for (TaskInfo info in taskInfoList!) {
if (info.status == DownloadTaskStatus.undefined) {
_requestDownload(info);
}
}
_permissionReady = await _checkPermission();
if (_permissionReady) {
await _prepareSaveDir();
}
setState(() {
_isLoading = false;
});
}
Future _prepareSaveDir() async {
_localPath = (await _findLocalPath())!;
final savedDir = Directory(_localPath);
bool hasExisted = await savedDir.exists();
if (!hasExisted) {
savedDir.create();
}
}
Future _findLocalPath() async {
var externalStorageDirPath;
if (Platform.isAndroid) {
try {
externalStorageDirPath = await AndroidPathProvider.downloadsPath;
} catch (e) {
final directory = await getExternalStorageDirectory();
externalStorageDirPath = directory?.path;
}
} else if (Platform.isIOS) {
externalStorageDirPath = (await getApplicationDocumentsDirectory()).absolute.path;
}
return externalStorageDirPath;
}
}
class DownloadItem extends StatelessWidget {
final ItemHolder? data;
final Function(TaskInfo?)? onItemClick;
final Function(TaskInfo)? onActionClick;
DownloadItem({this.data, this.onItemClick, this.onActionClick});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(left: 16.0, right: 8.0),
child: InkWell(
onTap: data!.task!.status == DownloadTaskStatus.complete
? () {
onItemClick!(data!.task);
}
: null,
child: Stack(
children: [
Container(
width: double.infinity,
height: 64.0,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Text(
data!.name!,
maxLines: 1,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: _buildActionForTask(data!.task!),
),
],
),
),
data!.task!.status == DownloadTaskStatus.running || data!.task!.status == DownloadTaskStatus.paused
? Positioned(
left: 0.0,
right: 0.0,
bottom: 0.0,
child: LinearProgressIndicator(
value: data!.task!.progress! / 100,
),
)
: Container()
].toList(),
),
),
);
}
Widget? _buildActionForTask(TaskInfo task) {
if (task.status == DownloadTaskStatus.undefined) {
return RawMaterialButton(
onPressed: () {
onActionClick!(task);
},
child: Icon(Icons.file_download),
shape: CircleBorder(),
constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.running) {
return RawMaterialButton(
onPressed: () {
onActionClick!(task);
},
child: Icon(
Icons.pause,
color: Colors.red,
),
shape: CircleBorder(),
constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.paused) {
return RawMaterialButton(
onPressed: () {
onActionClick!(task);
},
child: Icon(
Icons.play_arrow,
color: Colors.green,
),
shape: CircleBorder(),
constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
} else if (task.status == DownloadTaskStatus.complete) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: const [
Text(
'Completed',
style: TextStyle(color: Colors.green),
),
// RawMaterialButton(
// onPressed: () {
// onActionClick!(task);
// },
// child: Icon(
// Icons.delete_forever,
// color: Colors.red,
// ),
// shape: CircleBorder(),
// constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0),
// )
],
);
} else if (task.status == DownloadTaskStatus.canceled) {
return Text('Canceled', style: TextStyle(color: Colors.red));
} else if (task.status == DownloadTaskStatus.failed) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text('Failed', style: TextStyle(color: Colors.red)),
RawMaterialButton(
onPressed: () {
onActionClick!(task);
},
child: Icon(
Icons.refresh,
color: Colors.green,
),
shape: CircleBorder(),
constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0),
)
],
);
} else if (task.status == DownloadTaskStatus.enqueued) {
return Text('Pending', style: TextStyle(color: Colors.orange));
} else {
return null;
}
}
}
class TaskInfo {
final String? name;
final String? link;
String? taskId;
int? progress = 0;
String? localDownloadPath;
String? fileName;
DownloadTaskStatus? status = DownloadTaskStatus.undefined;
TaskInfo({this.name, this.link, this.localDownloadPath, this.fileName});
}
class ItemHolder {
final String? name;
final TaskInfo? task;
ItemHolder({this.name, this.task});
}
Заранее спасибо.
Подробнее здесь:
https://stackoverflow.com/questions/778 ... downlaoder
1719900829
Anonymous
Я использую flutter_downloader для загрузки файла по URL-адресу и проверил, что URL-адрес работает нормально и файл загружается, если я загружаю этот URL-адрес в браузере. [b]То же самое код работает отлично на iOS[/b] Я столкнулся с проблемой только в Android, файл начинает загружаться, но мгновенная загрузка не удалась. Я пытался связаться с GitHub с той же ошибкой, но не получил ответа. некоторые другие разработчики также получают эту ошибку, пожалуйста, проверьте здесь Я получаю следующие ошибки: [code]W/System.err(10971): java.lang.NullPointerException: httpConn.contentType must not be null W/System.err(10971): at vn.hunghd.flutterdownloader.DownloadWorker.downloadFile(DownloadWorker.kt:342) W/System.err(10971): at vn.hunghd.flutterdownloader.DownloadWorker.doWork(DownloadWorker.kt:208) W/System.err(10971): at androidx.work.Worker$1.run(Worker.java:86) W/System.err(10971): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) W/System.err(10971): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) W/System.err(10971): at java.lang.Thread.run(Thread.java:923) [/code] При инициализации я получаю следующую ошибку: [code]E/flutter (11803): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: 'package:flutter_downloader/src/downloader.dart': Failed assertion: line 49 pos 7: '!_initialized': plugin flutter_downloader has already been initialized E/flutter (11803): #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:51:61) E/flutter (11803): #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5) [/code] вот мой манифест с разрешениями: [code] android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider" android:authorities="im.mingguang.mingguang_app.flutter_downloader.provider" android:exported="false" android:grantUriPermissions="true" android:requestLegacyExternalStorage="true"> [/code] Ниже приведен код класса загрузки. [code]import 'dart:isolate'; import 'dart:ui'; import 'dart:async'; import 'dart:io'; import 'package:android_path_provider/android_path_provider.dart'; import 'package:device_info/device_info.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:permission_handler/permission_handler.dart'; const debug = true; List? taskInfoList = []; late List itemholderList = []; class DownloadFileScreen extends StatefulWidget with WidgetsBindingObserver { static const String routeName = "/DownloadFileScreen"; TargetPlatform? platform; DownloadFileScreen({Key? key}) : super(key: key); // final String? title; @override _DownloadFileScreenState createState() => new _DownloadFileScreenState(); } class _DownloadFileScreenState extends State { late bool _isLoading; late bool _permissionReady; late String _localPath; ReceivePort _port = ReceivePort(); @override void initState() { super.initState(); _checkPermission(); _bindBackgroundIsolate(); FlutterDownloader.registerCallback(downloadCallback); _isLoading = true; _permissionReady = false; _prepare(); } @override void dispose() { _unbindBackgroundIsolate(); super.dispose(); } void _bindBackgroundIsolate() async { await FlutterDownloader.initialize( debug: true, // optional: set to false to disable printing logs to console (default: true) ignoreSsl: true // option: set to false to disable working with http links (default: false) ); bool isSuccess = IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port'); if (!isSuccess) { _unbindBackgroundIsolate(); _bindBackgroundIsolate(); return; } _port.listen((dynamic data) async { if (debug) { print('UI Isolate Callback: $data'); } String? id = data[0]; DownloadTaskStatus? status = data[1] == 2 ? DownloadTaskStatus.running : data[1] == 4 ? DownloadTaskStatus.failed : data[1] == 3 ? DownloadTaskStatus.complete : DownloadTaskStatus.undefined; int? progress = data[2]; if (taskInfoList != null && taskInfoList!.isNotEmpty) { final task = taskInfoList!.firstWhere((task) => task.taskId == id); if (status == DownloadTaskStatus.complete) { await updateAoiRequest(taskInfoList, id); } setState(() { task.status = status; task.progress = progress; }); } }); FlutterDownloader.registerCallback(downloadCallback); } Future updateAoiRequest(List? taskInfoList, String? id) async { // final task = taskInfoList!.firstWhere((task) => task.taskId == id); // AoiRequest? aoiRequest = await AoiDBHelper.getAoiRequestByDownloadUrl(task.link!); // if (aoiRequest != null) { // aoiRequest.downloadStatus = AoiDownloadStatus.Complete; // AoiRequestBloc.databaseBloc.updateAoiRequest(aoiRequest); // } // print(aoiRequest?.fileName); } void _unbindBackgroundIsolate() { IsolateNameServer.removePortNameMapping('downloader_send_port'); } @pragma('vm:entry-point') // static void downloadCallback(String id, DownloadTaskStatus status, int progress) { static void downloadCallback(String id, status, int progress) { if (debug) { print('Background Isolate Callback: task ($id) is in status ($status) and process ($progress)'); } final SendPort send = IsolateNameServer.lookupPortByName('downloader_send_port')!; send.send([id, status, progress]); } @override Widget build(BuildContext context) { widget.platform = Theme.of(context).platform; return Scaffold( appBar: AppBar( title: Text("Download list"), ), body: Builder( builder: (context) => _isLoading ? Center( child: CircularProgressIndicator(), ) : _permissionReady ? _buildDownloadList() : _buildNoPermissionWarning()), ); } Widget _buildDownloadList() => Container( child: ListView( padding: const EdgeInsets.symmetric(vertical: 16.0), children: itemholderList .map((item) => item.task == null ? _buildListSection(item.name ?? 'Files') : DownloadItem( data: item, onItemClick: (task) { _openDownloadedFile(task).then((success) { if (!success) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Cannot open this file'))); } }); }, onActionClick: (task) { if (task.status == DownloadTaskStatus.undefined) { _requestDownload(task); } else if (task.status == DownloadTaskStatus.running) { _pauseDownload(task); } else if (task.status == DownloadTaskStatus.paused) { _resumeDownload(task); } else if (task.status == DownloadTaskStatus.complete) { _delete(task); } else if (task.status == DownloadTaskStatus.failed) { _retryDownload(task); } }, )) .toList(), ), ); Widget _buildListSection(String title) => Container( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: Text( title, style: TextStyle(fontWeight: FontWeight.bold, color: Colors.blue, fontSize: 18.0), ), ); Widget _buildNoPermissionWarning() => Container( child: Center( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), child: Text( 'Please grant accessing storage permission to continue -_-', textAlign: TextAlign.center, style: TextStyle(color: Colors.blueGrey, fontSize: 18.0), ), ), SizedBox( height: 32.0, ), TextButton( onPressed: () { _retryRequestPermission(); }, child: Text( 'Retry', style: TextStyle(color: Colors.blue, fontWeight: FontWeight.bold, fontSize: 20.0), )) ], ), ), ); Future _retryRequestPermission() async { final hasGranted = await _checkPermission(); if (hasGranted) { await _prepareSaveDir(); } setState(() { _permissionReady = hasGranted; }); } void _requestDownload(TaskInfo task) async { print(task.localDownloadPath); task.taskId = await FlutterDownloader.enqueue( url: task.link!, headers: {"content-type": "application/json"}, // headers: {"content-type": "multipart/form-data"}, fileName: task.fileName, savedDir: task.localDownloadPath!, showNotification: true, openFileFromNotification: true, // saveInPublicStorage: true, ); } void _cancelDownload(TaskInfo task) async { await FlutterDownloader.cancel(taskId: task.taskId!); } void _pauseDownload(TaskInfo task) async { await FlutterDownloader.pause(taskId: task.taskId!); } void _resumeDownload(TaskInfo task) async { String? newTaskId = await FlutterDownloader.resume(taskId: task.taskId!); task.taskId = newTaskId; } void _retryDownload(TaskInfo task) async { String? newTaskId = await FlutterDownloader.retry(taskId: task.taskId!); task.taskId = newTaskId; } Future _openDownloadedFile(TaskInfo? task) { if (task != null) { return FlutterDownloader.open(taskId: task.taskId!); } else { return Future.value(false); } } void _delete(TaskInfo task) async { await FlutterDownloader.remove(taskId: task.taskId!, shouldDeleteContent: true); await _prepare(); setState(() {}); } Future _checkPermission() async { print('check permission called'); if (Platform.isIOS) return true; DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; if (widget.platform == TargetPlatform.android && androidInfo.version.sdkInt // TaskInfo(name: document['name'], link: document['link']))); // // itemholderList.add(ItemHolder(name: 'Documents')); // for (int i = count; i < taskInfoList!.length; i++) { // itemholderList.add(ItemHolder(name: taskInfoList![i].name, task: taskInfoList![i])); // count++; // } // // taskInfoList!.addAll(_images // .map((image) => TaskInfo(name: image['name'], link: image['link']))); // // itemholderList.add(ItemHolder(name: 'Images')); // for (int i = count; i < taskInfoList!.length; i++) { // itemholderList.add(ItemHolder(name: taskInfoList![i].name, task: taskInfoList![i])); // count++; // } // // taskInfoList!.addAll(_videos // .map((video) => TaskInfo(name: video['name'], link: video['link']))); // // itemholderList.add(ItemHolder(name: 'Videos')); // for (int i = count; i < taskInfoList!.length; i++) { // itemholderList.add(ItemHolder(name: taskInfoList![i].name, task: taskInfoList![i])); // count++; // } tasks!.forEach((task) async { for (TaskInfo info in taskInfoList!) { if (info.link == task.url) { info.taskId = task.taskId; info.status = task.status; info.progress = task.progress; if (task.status == DownloadTaskStatus.complete) { await updateAoiRequest(taskInfoList, task.taskId); } else if (task.status == DownloadTaskStatus.undefined) { _requestDownload(info); } } } }); for (TaskInfo info in taskInfoList!) { if (info.status == DownloadTaskStatus.undefined) { _requestDownload(info); } } _permissionReady = await _checkPermission(); if (_permissionReady) { await _prepareSaveDir(); } setState(() { _isLoading = false; }); } Future _prepareSaveDir() async { _localPath = (await _findLocalPath())!; final savedDir = Directory(_localPath); bool hasExisted = await savedDir.exists(); if (!hasExisted) { savedDir.create(); } } Future _findLocalPath() async { var externalStorageDirPath; if (Platform.isAndroid) { try { externalStorageDirPath = await AndroidPathProvider.downloadsPath; } catch (e) { final directory = await getExternalStorageDirectory(); externalStorageDirPath = directory?.path; } } else if (Platform.isIOS) { externalStorageDirPath = (await getApplicationDocumentsDirectory()).absolute.path; } return externalStorageDirPath; } } class DownloadItem extends StatelessWidget { final ItemHolder? data; final Function(TaskInfo?)? onItemClick; final Function(TaskInfo)? onActionClick; DownloadItem({this.data, this.onItemClick, this.onActionClick}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.only(left: 16.0, right: 8.0), child: InkWell( onTap: data!.task!.status == DownloadTaskStatus.complete ? () { onItemClick!(data!.task); } : null, child: Stack( children: [ Container( width: double.infinity, height: 64.0, child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Text( data!.name!, maxLines: 1, softWrap: true, overflow: TextOverflow.ellipsis, ), ), Padding( padding: const EdgeInsets.only(left: 8.0), child: _buildActionForTask(data!.task!), ), ], ), ), data!.task!.status == DownloadTaskStatus.running || data!.task!.status == DownloadTaskStatus.paused ? Positioned( left: 0.0, right: 0.0, bottom: 0.0, child: LinearProgressIndicator( value: data!.task!.progress! / 100, ), ) : Container() ].toList(), ), ), ); } Widget? _buildActionForTask(TaskInfo task) { if (task.status == DownloadTaskStatus.undefined) { return RawMaterialButton( onPressed: () { onActionClick!(task); }, child: Icon(Icons.file_download), shape: CircleBorder(), constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0), ); } else if (task.status == DownloadTaskStatus.running) { return RawMaterialButton( onPressed: () { onActionClick!(task); }, child: Icon( Icons.pause, color: Colors.red, ), shape: CircleBorder(), constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0), ); } else if (task.status == DownloadTaskStatus.paused) { return RawMaterialButton( onPressed: () { onActionClick!(task); }, child: Icon( Icons.play_arrow, color: Colors.green, ), shape: CircleBorder(), constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0), ); } else if (task.status == DownloadTaskStatus.complete) { return Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: const [ Text( 'Completed', style: TextStyle(color: Colors.green), ), // RawMaterialButton( // onPressed: () { // onActionClick!(task); // }, // child: Icon( // Icons.delete_forever, // color: Colors.red, // ), // shape: CircleBorder(), // constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0), // ) ], ); } else if (task.status == DownloadTaskStatus.canceled) { return Text('Canceled', style: TextStyle(color: Colors.red)); } else if (task.status == DownloadTaskStatus.failed) { return Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ Text('Failed', style: TextStyle(color: Colors.red)), RawMaterialButton( onPressed: () { onActionClick!(task); }, child: Icon( Icons.refresh, color: Colors.green, ), shape: CircleBorder(), constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0), ) ], ); } else if (task.status == DownloadTaskStatus.enqueued) { return Text('Pending', style: TextStyle(color: Colors.orange)); } else { return null; } } } class TaskInfo { final String? name; final String? link; String? taskId; int? progress = 0; String? localDownloadPath; String? fileName; DownloadTaskStatus? status = DownloadTaskStatus.undefined; TaskInfo({this.name, this.link, this.localDownloadPath, this.fileName}); } class ItemHolder { final String? name; final TaskInfo? task; ItemHolder({this.name, this.task}); } [/code] Заранее спасибо. Подробнее здесь: [url]https://stackoverflow.com/questions/77821598/flutter-downloading-failed-always-using-flutter-downlaoder[/url]