Skip to content

Instantly share code, notes, and snippets.

@etozzato
Created January 11, 2022 23:41
Show Gist options
  • Save etozzato/121efa9451e245f635615d634809f285 to your computer and use it in GitHub Desktop.
Save etozzato/121efa9451e245f635615d634809f285 to your computer and use it in GitHub Desktop.
Asset downloader for Flutter apps
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:download_assets/download_assets.dart';
enum DownloaderOrientation { vertical, horizontal }
enum DownloaderStatus { ready, downloading, downloaded, error }
class Downloader extends StatefulWidget {
final DownloaderOrientation orientation;
final String assetURL;
final String assetFileName;
final String notDownloaded;
final String downloading;
final String downloaded;
final String downloadError;
final String downloadDirectory;
final String labelCancel;
final String labelYes;
final Color textColor;
final Color pressedTextColor;
final String deleteConfirmationTitle;
final String deleteConfirmationMessage;
final String downloadConfirmationTitle;
final String downloadConfirmationMessage;
const Downloader(
{Key key,
this.orientation,
this.assetURL,
this.assetFileName,
this.notDownloaded = "not downloaded",
this.downloading = "downloading PROGRESS",
this.downloaded = "downloaded FILESIZE",
this.downloadError = "download error",
this.downloadDirectory = "tensorflow",
this.labelCancel = "Cancel",
this.labelYes = "Yes",
this.deleteConfirmationTitle = "Delete",
this.deleteConfirmationMessage =
"Are you sure you want to delete this file?",
this.downloadConfirmationTitle = "Download",
this.downloadConfirmationMessage =
"Are you sure you want to download this file?",
this.textColor = Colors.grey,
this.pressedTextColor = Colors.deepOrangeAccent})
: super(key: key);
@override
DownloaderState createState() => DownloaderState();
}
class DownloaderState extends State<Downloader> {
DownloaderStatus _status = DownloaderStatus.ready;
double _progress = 0;
String _fileSize;
bool isPressed = false;
@override
void initState() {
super.initState();
prepareDownload();
}
@override
void dispose() {
super.dispose();
}
void prepareDownload() async {
await DownloadAssetsController.init(directory: widget.downloadDirectory);
isDownloaded();
}
Color colorForStatus() {
switch (_status) {
case DownloaderStatus.ready:
return Colors.blue;
case DownloaderStatus.downloading:
return Colors.orange;
case DownloaderStatus.downloaded:
return Colors.green;
case DownloaderStatus.error:
return Colors.red;
default:
return Colors.transparent;
}
}
String msgForStatus() {
switch (_status) {
case DownloaderStatus.ready:
return widget.notDownloaded;
case DownloaderStatus.downloading:
return widget.downloading
.replaceAll("PROGRESS", " ${_progress.toInt()}%");
case DownloaderStatus.downloaded:
return widget.downloaded
.replaceAll("FILESIZE", _fileSize != null ? " $_fileSize" : "");
case DownloaderStatus.error:
return widget.downloadError;
default:
return "";
}
}
static String formatBytes(int bytes, int decimals) {
if (bytes <= 0) return "0 B";
const suffixes = ["B", "KB", "MB", "GB"];
var i = (log(bytes) / log(1024)).floor();
return ((bytes / pow(1024, i)).toStringAsFixed(decimals)) +
' ' +
suffixes[i];
}
Future<bool> isDownloaded() async {
bool assetsDownloaded =
await DownloadAssetsController.assetsFileExists(widget.assetFileName);
if (assetsDownloaded) {
final file =
File("${DownloadAssetsController.assetsDir}/${widget.assetFileName}");
setState(() {
_status = DownloaderStatus.downloaded;
_fileSize = formatBytes(file.lengthSync(), 2);
});
return true;
} else {
return false;
}
}
void deleteAssets() async {
await DownloadAssetsController.clearAssets();
setState(() {
_status = DownloaderStatus.ready;
_fileSize = null;
});
}
void downloadAsset() async {
final assetsDownloaded = await isDownloaded();
if (assetsDownloaded) {
return;
}
setState(() {
_status = DownloaderStatus.downloading;
});
try {
await DownloadAssetsController.startDownload(
assetsUrl: widget.assetURL,
onProgress: (_progressValue) {
setState(() {
_progress = _progressValue;
});
},
onComplete: () async {
await isDownloaded();
setState(() {
_status = DownloaderStatus.downloaded;
});
},
onError: (exception) {
setState(() {
_status = DownloaderStatus.error;
});
});
} on DownloadAssetsException {
setState(() {
_status = DownloaderStatus.error;
});
}
}
Future<bool> _confirmationDialog(String title, String content) async {
return showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text(title),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('content'),
],
),
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, false),
child: Text(widget.labelCancel),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: Text(widget.labelYes),
),
],
);
},
);
}
void onLongPress() async {
final assetsDownloaded = await isDownloaded();
if (assetsDownloaded) {
final confirmed = await _confirmationDialog(
widget.deleteConfirmationTitle, widget.deleteConfirmationMessage);
if (confirmed) {
deleteAssets();
}
} else {
final confirmed = await _confirmationDialog(
widget.downloadConfirmationTitle, widget.downloadConfirmationMessage);
if (confirmed) {
downloadAsset();
}
}
setState(() {
isPressed = false;
});
}
@override
Widget build(BuildContext context) {
Widget orientedWidget(List<Widget> children) {
return widget.orientation == DownloaderOrientation.horizontal
? Row(children: children, mainAxisAlignment: MainAxisAlignment.center)
: Column(
children: children, mainAxisAlignment: MainAxisAlignment.center);
}
return GestureDetector(
onLongPress: () {
onLongPress();
},
onLongPressDown: (_) {
setState(() {
isPressed = true;
});
},
onLongPressCancel: () {
setState(() {
isPressed = false;
});
},
child: orientedWidget(
[
AnimatedContainer(
duration: const Duration(milliseconds: 750),
curve: Curves.easeInOutCubic,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: colorForStatus(),
),
child: const SizedBox(width: 8, height: 16)),
const SizedBox(width: 8),
Text(msgForStatus(),
style: TextStyle(
color:
isPressed ? widget.pressedTextColor : widget.textColor)),
],
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment