simple_downloader 0.0.3+1
simple_downloader: ^0.0.3+1 copied to clipboard
flutter plugin for handling download file using http with pause, resume, cancel, retry, restart & open file downloader features.
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:path_provider/path_provider.dart' as path;
import 'package:simple_downloader/simple_downloader.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late SimpleDownloader _downloader;
double _progress = 0.0;
int _offset = 0;
int _total = 0;
DownloadStatus _status = DownloadStatus.undefined;
DownloaderTask _task = const DownloaderTask(
url:
"https://images.unsplash.com/photo-1615220368123-9bb8faf4221b?ixlib=rb-1.2.1&dl=vadim-kaipov-f6jkAE1ZWuY-unsplash.jpg&q=80&fm=jpg&crop=entropy&cs=tinysrgb",
fileName: "images_downloaded.jpg",
bufferSize:
1024, // if bufferSize value not set, default value is 64 ( 64 Kb )
);
@override
void initState() {
super.initState();
init();
}
@override
void dispose() {
_downloader.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
height: 150,
width: double.maxFinite,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(
color: Colors.black12,
offset: Offset(0, 3),
spreadRadius: 3,
blurRadius: 3,
)
]),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
ListTile(
visualDensity:
const VisualDensity(horizontal: -4, vertical: 0),
title: Text(_task.fileName ?? ""),
subtitle: labelStatus,
trailing: trailingIcon,
),
const SizedBox(height: 25.0),
Padding(
padding: const EdgeInsets.only(left: 15.0, right: 15.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("${_offset ~/ 1024} Kb / ${_total ~/ 1024} Kb",
textAlign: TextAlign.right),
Text("${_progress.floor()} %",
textAlign: TextAlign.right),
],
),
const SizedBox(height: 5),
LinearProgressIndicator(
value: _progress / 100,
)
],
),
),
],
),
),
),
),
);
}
Future<void> init() async {
final pathFile = (await path.getExternalStorageDirectory())!.path;
if (!mounted) return;
_task = _task.copyWith(
downloadPath: pathFile,
);
_downloader = SimpleDownloader.init(task: _task);
_downloader.callback.addListener(() {
setState(() {
_progress = _downloader.callback.progress;
_status = _downloader.callback.status;
_total = _downloader.callback.total;
_offset = _downloader.callback.offset;
});
});
}
Widget get labelStatus {
switch (_status) {
case DownloadStatus.undefined:
return const Text("Waiting..");
case DownloadStatus.completed:
return const Text("Download Completed");
case DownloadStatus.failed:
return const Text("Download Failed");
case DownloadStatus.paused:
return const Text("Download Paused");
case DownloadStatus.deleted:
return const Text("File Deleted");
case DownloadStatus.canceled:
return const Text("Download Canceled");
default:
return const Text("Downloading");
}
}
Widget? get trailingIcon {
if (_status == DownloadStatus.undefined ||
_status == DownloadStatus.deleted) {
return IconButton(
splashRadius: 20,
onPressed: () => _downloader.download(),
constraints: const BoxConstraints(minHeight: 32, minWidth: 32),
icon: const Icon(
Icons.play_arrow,
),
);
}
if (_status == DownloadStatus.running ||
_status == DownloadStatus.resume ||
_status == DownloadStatus.retry) {
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
IconButton(
splashRadius: 20,
onPressed: () => _downloader.pause(),
constraints: const BoxConstraints(minHeight: 32, minWidth: 32),
icon: const Icon(
Icons.pause,
color: Colors.green,
),
),
IconButton(
splashRadius: 20,
onPressed: () => _downloader.cancel(),
constraints: const BoxConstraints(minHeight: 32, minWidth: 32),
icon: const Icon(
Icons.close,
color: Colors.red,
),
),
],
);
}
if (_status == DownloadStatus.paused) {
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
IconButton(
splashRadius: 20,
onPressed: () => _downloader.resume(),
constraints: const BoxConstraints(minHeight: 32, minWidth: 32),
icon: const Icon(
Icons.play_arrow,
color: Colors.blue,
),
),
IconButton(
splashRadius: 20,
onPressed: () => _downloader.cancel(),
constraints: const BoxConstraints(minHeight: 32, minWidth: 32),
icon: const Icon(
Icons.close,
color: Colors.red,
),
),
],
);
}
if (_status == DownloadStatus.completed) {
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
IconButton(
tooltip: "Open file",
splashRadius: 20,
onPressed: () async {
_downloader.open().then(
(isSuccess) {
if (!isSuccess!) {
showMessage(context, "Failed to open downloaded file.");
}
},
);
},
constraints: const BoxConstraints(minHeight: 32, minWidth: 32),
icon: const Icon(
Icons.file_open,
color: Colors.green,
),
),
IconButton(
tooltip: "Delete file",
splashRadius: 20,
onPressed: () => _downloader.delete(),
constraints: const BoxConstraints(minHeight: 32, minWidth: 32),
icon: const Icon(
Icons.delete,
color: Colors.red,
),
),
],
);
}
if (_status == DownloadStatus.failed ||
_status == DownloadStatus.canceled) {
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
IconButton(
splashRadius: 20,
onPressed: () => _downloader.retry(),
constraints: const BoxConstraints(minHeight: 32, minWidth: 32),
icon: const Icon(
Icons.refresh,
color: Colors.green,
),
),
IconButton(
splashRadius: 20,
onPressed: () => _downloader.restart(),
constraints: const BoxConstraints(minHeight: 32, minWidth: 32),
icon: const Icon(
Icons.restart_alt,
color: Colors.red,
),
),
]);
}
return null;
}
Future<void> showMessage(
BuildContext context,
String message,
) async {
final size = MediaQuery.of(context).size;
return showModalBottomSheet(
elevation: 2,
isDismissible: false,
enableDrag: false,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30), topRight: Radius.circular(30)),
),
context: context,
builder: (builder) {
return SizedBox(
height: 250,
child: Stack(children: [
Positioned(
top: 10,
right: 10,
child: IconButton(
splashRadius: 25,
onPressed: () => Navigator.pop(context),
icon: const Icon(
Icons.close,
size: 25,
),
),
),
Center(
child: Container(
constraints: BoxConstraints(
maxWidth: 0.8 * size.width,
),
child: Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
),
Positioned(
bottom: 20,
right: 20,
left: 20,
child: ElevatedButton(
child: const Text(
"OK",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.w600),
),
onPressed: () => Navigator.pop(context),
),
),
]),
);
});
}
}