flutter_omni_downloader 0.1.0
flutter_omni_downloader: ^0.1.0 copied to clipboard
Request-aware Flutter downloader with resumable transfers and background support.
example/lib/main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_omni_downloader/flutter_omni_downloader.dart';
void main() {
runApp(const DownloaderExampleApp());
}
class DownloaderExampleApp extends StatelessWidget {
const DownloaderExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Omni Downloader',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF0B6E4F)),
useMaterial3: true,
),
home: const DownloaderHomePage(),
);
}
}
class DownloaderHomePage extends StatefulWidget {
const DownloaderHomePage({super.key});
@override
State<DownloaderHomePage> createState() => _DownloaderHomePageState();
}
class _DownloaderHomePageState extends State<DownloaderHomePage> {
final TextEditingController _urlController = TextEditingController(
text: 'https://speed.hetzner.de/100MB.bin',
);
final TextEditingController _tokenController = TextEditingController();
StreamSubscription<DownloadProgress>? _subscription;
DownloadProgress? _progress;
String? _taskId;
@override
void initState() {
super.initState();
_subscription = UniversalDownloader.progressStream.listen((event) {
if (!mounted) {
return;
}
setState(() {
_progress = event;
});
});
}
@override
void dispose() {
_subscription?.cancel();
_urlController.dispose();
_tokenController.dispose();
super.dispose();
}
Future<void> _startDownload() async {
final headers = <String, String>{
'Accept': '*/*',
};
if (_tokenController.text.isNotEmpty) {
headers['Authorization'] = 'Bearer ${_tokenController.text}';
}
final taskId = await UniversalDownloader.start(
DownloadRequest(
url: _urlController.text,
fileName: 'sample_${DateTime.now().millisecondsSinceEpoch}.bin',
headers: headers,
enableResume: true,
enableBackground: true,
showNotification: true,
),
);
setState(() {
_taskId = taskId;
});
}
@override
Widget build(BuildContext context) {
final progress = _progress;
return Scaffold(
appBar: AppBar(title: const Text('Omni Downloader')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextField(
controller: _urlController,
decoration: const InputDecoration(
labelText: 'Download URL',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
TextField(
controller: _tokenController,
decoration: const InputDecoration(
labelText: 'Bearer token (optional)',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
FilledButton(
onPressed: _startDownload,
child: const Text('Start download'),
),
const SizedBox(height: 16),
if (_taskId != null) ...<Widget>[
Text('Task: $_taskId'),
const SizedBox(height: 8),
Row(
children: <Widget>[
Expanded(
child: OutlinedButton(
onPressed: () => UniversalDownloader.pause(_taskId!),
child: const Text('Pause'),
),
),
const SizedBox(width: 12),
Expanded(
child: OutlinedButton(
onPressed: () => UniversalDownloader.resume(_taskId!),
child: const Text('Resume'),
),
),
const SizedBox(width: 12),
Expanded(
child: OutlinedButton(
onPressed: () => UniversalDownloader.cancel(_taskId!),
child: const Text('Cancel'),
),
),
],
),
],
const SizedBox(height: 24),
if (progress != null) ...<Widget>[
LinearProgressIndicator(value: progress.progress / 100),
const SizedBox(height: 12),
Text('Status: ${progress.status.name}'),
Text('Progress: ${progress.progress}%'),
Text(
'Bytes: ${progress.downloadedBytes} / ${progress.totalBytes}',
),
Text('Speed: ${progress.speed.toStringAsFixed(2)} MB/s'),
],
],
),
),
);
}
}