I am trying to develop an app that downloads files from URLs, such as images, pdf, videos and etc, I am using Android 12 for Mobile device and these Plugin :
path_provider_android: ^2.0.12
flutter_downloader: ^1.7.3
permission_handler: ^9.2.0
device_info_plus: ^3.2.3
path_provider: ^2.0.9
This is the main.dart file
import 'package:download_file_app/my_download.dart';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
const debug = true;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize(
debug: debug // optional: set false to disable printing logs to console
);
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final platform = Theme.of(context).platform;
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyDownload(
platform: platform,
),
);
}
}
And this is the MyDownload Class
class MyDownload extends StatefulWidget {
final TargetPlatform? platform;
MyDownload({this.platform});
@override
_MyDownloadState createState() => _MyDownloadState();
}
class _MyDownloadState extends State<MyDownload> {
ReceivePort _port = ReceivePort();
late bool _isLoading;
late bool _permissionReady;
late String _localPath;
final _documents = [
{
'name': 'Learning Android Studio',
'link': 'http://barbra-coco.dyndns.org/student/learning_android_studio.pdf'
},
{
'name': 'Canyonlands National Park',
'link': 'https://upload.wikimedia.org/wikipedia/commons/7/78/Canyonlands_National_Park%E2%80%A6Needles_area_%286294480744%29.jpg'
},
{
'name': 'Big Buck Bunny',
'link':
'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'
},
];
late List<MyItem> itemsList;
@override
void initState() {
super.initState();
_bindBackgroundIsolate();
FlutterDownloader.registerCallback(downloadCallback);
_isLoading = true;
_permissionReady = false;
_prepare();
}
void _bindBackgroundIsolate() {
bool isSuccess = IsolateNameServer.registerPortWithName(
_port.sendPort, 'downloader_send_port');
if (!isSuccess) {
_unbindBackgroundIsolate();
_bindBackgroundIsolate();
return;
}
_port.listen((dynamic data) {
if (debug) {
print('UI Isolate Callback: $data');
}
/*
Update UI with the latest progress
*/
String? id = data[0];
DownloadTaskStatus? status = data[1];
int progress = data[2];
if (itemsList.isNotEmpty ) {
final item = itemsList.firstWhere((it) => it.itemID == id);
setState(() {
item.status = status;
item.progress = progress;
});
}
});
}
void _unbindBackgroundIsolate() {
IsolateNameServer.removePortNameMapping('downloader_send_port');
}
static void downloadCallback(String id, DownloadTaskStatus 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]);
}
Future<Null> _prepare() async {
itemsList = [];
itemsList.addAll(
_documents.map((doc) => MyItem(name: doc['name'].toString(),
url: doc['link'].toString())
)
);
_permissionReady = await _checkPermission();
if (_permissionReady) {
await _prepareSaveDir();
}
setState(() {
_isLoading = false;
});
}
Future<bool> _checkPermission() async {
if (Platform.isIOS) return true;
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
if (widget.platform == TargetPlatform.android )
// && androidInfo.version.sdkInt! <= 28)
{
final status = await Permission.storage.status;
if (status != PermissionStatus.granted) {
final result = await Permission.storage.request();
if (result == PermissionStatus.granted) {
return true;
}
} else {
return true;
}
} else {
return true;
}
return false;
}
Future<void> _prepareSaveDir() async {
_localPath = (await _findLocalPath())!;
final savedDir = Directory(_localPath);
bool hasExisted = await savedDir.exists();
if (!hasExisted) {
savedDir.create();
}
}
Future<String?> _findLocalPath() async {
var externalStorageDirPath;
if (Platform.isAndroid) {
try {
externalStorageDirPath = await PathProviderAndroid().getDownloadsPath(); //AndroidPathProvider.downloadsPath;
} catch (e) {
final directory = await getExternalStorageDirectory();
externalStorageDirPath = directory?.path;
}
} else if (Platform.isIOS) {
externalStorageDirPath =
(await getApplicationDocumentsDirectory()).absolute.path;
}
return externalStorageDirPath;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Download File App'),
),
body: Builder(
builder: (context) =>
_isLoading
? Center(child: new CircularProgressIndicator(),)
: _permissionReady ?
Center(child:
ListView(
children: itemsList.map((it) =>
DownloadITem(
myItem: it,
openItem: (myItem)
{
_openDownloadedFile(myItem).then((success) {
if (!success) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('Cannot open this file')));
}
});
},
onActionClick: (myItem)async {
if (myItem.status == DownloadTaskStatus.undefined) {
myItem.itemID = await FlutterDownloader.enqueue(
url: myItem.url,
savedDir: _localPath,
showNotification: true,
openFileFromNotification: true,
saveInPublicStorage: true,
);
//_requestDownload(myItem);
}
else if (myItem.status == DownloadTaskStatus.running) {
await FlutterDownloader.pause(taskId: myItem.itemID!);
// _pauseDownload(myItem);
}
else if (myItem.status == DownloadTaskStatus.paused) {
String? newTaskId = await FlutterDownloader.resume(taskId: myItem.itemID!);
myItem.itemID = newTaskId;
//_resumeDownload(myItem);
}
else if (myItem.status == DownloadTaskStatus.complete) {
await FlutterDownloader.remove(
taskId: myItem.itemID!, shouldDeleteContent: true);
await _prepare();
setState(() {});
// _delete(myItem);
}
else if (myItem.status == DownloadTaskStatus.failed) {
String? newTaskId = await FlutterDownloader.retry(taskId: myItem.itemID!);
myItem.itemID = newTaskId;
//_retryDownload(myItem);
}
}
),).toList()),)
:
Container(),
));
}
Future<bool> _openDownloadedFile(MyItem item) {
if (item != null) {
return FlutterDownloader.open(taskId: item.itemID!);
} else {
return Future.value(false);
}
}
void _requestDownload(MyItem item) async {
item.itemID = await FlutterDownloader.enqueue(
url: item.url,
savedDir: _localPath,
showNotification: true,
openFileFromNotification: true,
saveInPublicStorage: true,
);
}
void _pauseDownload(MyItem item) async {
await FlutterDownloader.pause(taskId: item.itemID!);
}
void _retryDownload(MyItem item) async {
String? newTaskId = await FlutterDownloader.retry(taskId: item.itemID!);
item.itemID = newTaskId;
}
void _resumeDownload(MyItem item) async {
String? newTaskId = await FlutterDownloader.resume(taskId: item.itemID!);
item.itemID = newTaskId;
}
void _delete(MyItem item) async {
await FlutterDownloader.remove(
taskId: item.itemID!, shouldDeleteContent: true);
await _prepare();
setState(() {});
}
@override
void dispose() {
_unbindBackgroundIsolate();
super.dispose();
}
}
class DownloadITem extends StatelessWidget {
final MyItem myItem;
final Function(MyItem) openItem;
final Function(MyItem) onActionClick;
DownloadITem(
{required this.myItem,
required this.openItem,
required this.onActionClick});
@override
Widget build(BuildContext context) {
return Container(
//color: Colors.pink[50],
padding: const EdgeInsets.only(left: 16.0, right: 8.0),
child: InkWell(
onTap: myItem.status == DownloadTaskStatus.complete
? () {
openItem(myItem);
}
: null,
child:
Stack(
children: <Widget>[
Container(
width: double.infinity,
height: 64.0,
// color: Colors.amber,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Text(myItem.name,),
),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: _buildActionForTask(myItem),
),
],
),
),
Positioned(
left: 0.0,
right: 0.0,
bottom: 0.0,
child: LinearProgressIndicator(
value: myItem.progress / 100,
),
)
//: Container()
].toList(),
),
),
);
}
Widget? _buildActionForTask(MyItem item) {
if (item.status == DownloadTaskStatus.undefined) {
return RawMaterialButton(
onPressed: () {
onActionClick(item);
},
child: Icon(Icons.file_download),
shape: CircleBorder(),
constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
}
else if (item.status == DownloadTaskStatus.running) {
return RawMaterialButton(
onPressed: () {
onActionClick(item);
},
child: Icon(
Icons.pause,
color: Colors.red,
),
shape: CircleBorder(),
constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
}
else if (item.status == DownloadTaskStatus.paused) {
return RawMaterialButton(
onPressed: () {
onActionClick(item);
},
child: Icon(
Icons.play_arrow,
color: Colors.green,
),
shape: CircleBorder(),
constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0),
);
}
else if (item.status == DownloadTaskStatus.complete) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
'Ready',
style: TextStyle(color: Colors.green),
),
RawMaterialButton(
onPressed: () {
onActionClick(item);
},
child: Icon(
Icons.delete_forever,
color: Colors.red,
),
shape: CircleBorder(),
constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0),
)
],
);
}
else if (item.status == DownloadTaskStatus.failed) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text('Failed', style: TextStyle(color: Colors.red)),
RawMaterialButton(
onPressed: () {
onActionClick(item);
},
child: Icon(
Icons.refresh,
color: Colors.green,
),
shape: CircleBorder(),
constraints: BoxConstraints(minHeight: 32.0, minWidth: 32.0),
)
],
);
}
// else if (item.status == DownloadTaskStatus.canceled) {
// return Text('Canceled', style: TextStyle(color: Colors.red));
// }
// else if (item.status == DownloadTaskStatus.enqueued) {
// return Text('Pending', style: TextStyle(color: Colors.orange));
// }
else {
return null;
}
}
}
the source code is on GitHub
https://github.com/sunaarun/Download_files_Flutter
And these are the messages shown on the Console it show that there is exception
Expecting binder but got null!
Please Help