flutter_any_download 1.1.1
flutter_any_download: ^1.1.1 copied to clipboard
Download any file and open Android and iOS
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_any_download/flutter_any_download.dart';
import 'package:open_filex/open_filex.dart';
import 'dart:io';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Any Download',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
fontFamily: 'Inter',
scaffoldBackgroundColor: const Color(0xFFF8FAFC),
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF3B82F6),
brightness: Brightness.light,
),
),
home: const DownloadDemoPage(),
);
}
}
class DownloadDemoPage extends StatefulWidget {
const DownloadDemoPage({super.key});
@override
State<DownloadDemoPage> createState() => _DownloadDemoPageState();
}
class _DownloadDemoPageState extends State<DownloadDemoPage>
with TickerProviderStateMixin {
final FlutterAnyDownload _downloader = FlutterAnyDownload();
final TextEditingController _urlController = TextEditingController();
final TextEditingController _filenameController = TextEditingController();
bool _isDownloading = false;
double _progress = 0.0;
String _status = 'Ready to download';
String? _downloadedFilePath;
late AnimationController _pulseController;
late AnimationController _successController;
late Animation<double> _pulseAnimation;
late Animation<double> _successAnimation;
@override
void initState() {
super.initState();
_initializeDownloader();
_setupAnimations();
// Sample URL - PDF file
_urlController.text =
'https://www.princexml.com/samples/icelandic/dictionary.pdf';
_filenameController.text = 'dictionary.pdf';
}
void _setupAnimations() {
_pulseController = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
_successController = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_pulseAnimation = Tween<double>(begin: 1.0, end: 1.1).animate(
CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut),
);
_successAnimation = CurvedAnimation(
parent: _successController,
curve: Curves.elasticOut,
);
}
Future<void> _initializeDownloader() async {
await _downloader.initialize();
await _downloader.requestNotificationPermission();
}
Future<void> _startDownload() async {
if (_urlController.text.isEmpty || _filenameController.text.isEmpty) {
_showSnackBar('Please enter URL and filename', isError: true);
return;
}
setState(() {
_isDownloading = true;
_progress = 0.0;
_status = 'Initializing download...';
_downloadedFilePath = null;
});
final result = await _downloader.downloadFile(
url: _urlController.text,
filename: _filenameController.text,
saveToDownloadsFolder: true,
showNotification: true,
onProgress: (downloaded, total) {
setState(() {
_progress = downloaded / total;
final downloadedMB = (downloaded / 1024 / 1024).toStringAsFixed(2);
final totalMB = (total / 1024 / 1024).toStringAsFixed(2);
_status = 'Downloading $downloadedMB MB / $totalMB MB';
});
},
onComplete: (filePath) {
setState(() {
_isDownloading = false;
_status = 'Download completed successfully! 🎉';
_downloadedFilePath = filePath;
});
_successController.forward(from: 0);
_showSnackBar('Download completed!', isSuccess: true);
},
onError: (error) {
setState(() {
_isDownloading = false;
_status = 'Download failed';
});
_showSnackBar('Download failed: $error', isError: true);
},
);
if (!result.success) {
setState(() {
_isDownloading = false;
_status = result.message;
});
}
}
Future<void> _openDownloadedFile() async {
if (_downloadedFilePath == null) {
_showSnackBar('No file available to open', isError: true);
return;
}
try {
final file = File(_downloadedFilePath!);
if (!await file.exists()) {
_showSnackBar('File not found', isError: true);
return;
}
final result = await OpenFilex.open(_downloadedFilePath!);
if (result.type == ResultType.done) {
_showSnackBar('File opened successfully', isSuccess: true);
} else if (result.type == ResultType.noAppToOpen) {
_showSnackBar('No app available to open this file', isError: true);
} else {
_showSnackBar('Could not open file', isError: true);
}
} catch (e) {
_showSnackBar('Error opening file: $e', isError: true);
}
}
void _showSnackBar(String message,
{bool isError = false, bool isSuccess = false}) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
Icon(
isSuccess
? Icons.check_circle
: isError
? Icons.error
: Icons.info,
color: Colors.white,
),
const SizedBox(width: 12),
Expanded(child: Text(message)),
],
),
backgroundColor: isSuccess
? const Color(0xFF10B981)
: isError
? const Color(0xFFEF4444)
: const Color(0xFF3B82F6),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
duration: const Duration(seconds: 3),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFF8FAFC),
Color(0xFFEFF6FF),
],
),
),
child: SafeArea(
child: CustomScrollView(
slivers: [
_buildAppBar(),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
_buildInputSection(),
const SizedBox(height: 24),
_buildProgressSection(),
const SizedBox(height: 24),
_buildActionButtons(),
const SizedBox(height: 32),
_buildFeaturesList(),
],
),
),
),
],
),
),
),
);
}
Widget _buildAppBar() {
return SliverAppBar(
expandedHeight: 140,
floating: false,
pinned: true,
elevation: 0,
backgroundColor: Colors.transparent,
flexibleSpace: FlexibleSpaceBar(
title: const Text(
'Flutter Any Download',
style: TextStyle(
color: Color(0xFF1E293B),
fontWeight: FontWeight.bold,
),
),
background: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
const Color(0xFF3B82F6).withOpacity(0.1),
const Color(0xFF8B5CF6).withOpacity(0.1),
],
),
),
child: Center(
child: Icon(
Icons.cloud_download_rounded,
size: 80,
color: const Color(0xFF3B82F6).withOpacity(0.3),
),
),
),
),
);
}
Widget _buildInputSection() {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: const Color(0xFF3B82F6).withOpacity(0.08),
blurRadius: 20,
offset: const Offset(0, 4),
),
],
),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF3B82F6), Color(0xFF8B5CF6)],
),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(
Icons.settings_input_component,
color: Colors.white,
size: 24,
),
),
const SizedBox(width: 12),
const Text(
'Configuration',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xFF1E293B),
),
),
],
),
const SizedBox(height: 24),
_buildModernTextField(
controller: _urlController,
label: 'Download URL',
hint: 'https://example.com/file.pdf',
icon: Icons.link_rounded,
maxLines: 2,
),
const SizedBox(height: 16),
_buildModernTextField(
controller: _filenameController,
label: 'Filename',
hint: 'my_file.pdf',
icon: Icons.insert_drive_file_rounded,
),
],
),
),
);
}
Widget _buildModernTextField({
required TextEditingController controller,
required String label,
required String hint,
required IconData icon,
int maxLines = 1,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF64748B),
),
),
const SizedBox(height: 8),
Container(
decoration: BoxDecoration(
color: const Color(0xFFF1F5F9),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE2E8F0)),
),
child: TextField(
controller: controller,
maxLines: maxLines,
style: const TextStyle(
fontSize: 15,
color: Color(0xFF1E293B),
),
decoration: InputDecoration(
hintText: hint,
hintStyle: TextStyle(color: Colors.grey[400]),
prefixIcon: Icon(icon, color: const Color(0xFF3B82F6)),
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
),
),
),
],
);
}
Widget _buildProgressSection() {
if (!_isDownloading && _downloadedFilePath == null) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: const Color(0xFF3B82F6).withOpacity(0.08),
blurRadius: 20,
offset: const Offset(0, 4),
),
],
),
padding: const EdgeInsets.all(32),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
const Color(0xFF3B82F6).withOpacity(0.1),
const Color(0xFF8B5CF6).withOpacity(0.1),
],
),
shape: BoxShape.circle,
),
child: const Icon(
Icons.download_rounded,
size: 48,
color: Color(0xFF3B82F6),
),
),
const SizedBox(height: 16),
Text(
_status,
style: const TextStyle(
fontSize: 16,
color: Color(0xFF64748B),
fontWeight: FontWeight.w500,
),
),
],
),
);
}
if (_isDownloading) {
return AnimatedBuilder(
animation: _pulseAnimation,
builder: (context, child) {
return Transform.scale(
scale: _pulseAnimation.value,
child: Container(
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF3B82F6), Color(0xFF8B5CF6)],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: const Color(0xFF3B82F6).withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
children: [
Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: 120,
height: 120,
child: CircularProgressIndicator(
value: _progress,
strokeWidth: 8,
backgroundColor: Colors.white.withOpacity(0.3),
valueColor: const AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
),
Text(
'${(_progress * 100).toStringAsFixed(0)}%',
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
const SizedBox(height: 24),
const Text(
'Downloading...',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
_status,
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.9),
),
textAlign: TextAlign.center,
),
],
),
),
),
);
},
);
}
// Success state
return ScaleTransition(
scale: _successAnimation,
child: Container(
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF10B981), Color(0xFF059669)],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: const Color(0xFF10B981).withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
padding: const EdgeInsets.all(32),
child: Column(
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: const Icon(
Icons.check_rounded,
size: 48,
color: Color(0xFF10B981),
),
),
const SizedBox(height: 16),
const Text(
'Download Complete!',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
_status,
style: TextStyle(
fontSize: 14,
color: Colors.white.withOpacity(0.9),
),
textAlign: TextAlign.center,
),
if (_downloadedFilePath != null) ...[
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
),
child: Text(
_downloadedFilePath!,
style: const TextStyle(
fontSize: 12,
color: Colors.white,
fontFamily: 'monospace',
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
],
),
),
);
}
Widget _buildActionButtons() {
return Column(
children: [
_buildGradientButton(
onPressed: _isDownloading ? null : _startDownload,
icon: Icons.download_rounded,
label: 'Start Download',
gradient: const LinearGradient(
colors: [Color(0xFF3B82F6), Color(0xFF8B5CF6)],
),
),
if (_downloadedFilePath != null) ...[
const SizedBox(height: 12),
_buildGradientButton(
onPressed: _openDownloadedFile,
icon: Icons.folder_open_rounded,
label: 'Open Downloaded File',
gradient: const LinearGradient(
colors: [Color(0xFF10B981), Color(0xFF059669)],
),
),
],
if (_isDownloading) ...[
const SizedBox(height: 12),
_buildOutlineButton(
onPressed: () {
_downloader.cancelAllDownloads();
setState(() {
_isDownloading = false;
_status = 'Download cancelled';
});
},
icon: Icons.cancel_rounded,
label: 'Cancel Download',
),
],
],
);
}
Widget _buildGradientButton({
required VoidCallback? onPressed,
required IconData icon,
required String label,
required Gradient gradient,
}) {
return Container(
decoration: BoxDecoration(
gradient: onPressed != null ? gradient : null,
color: onPressed == null ? Colors.grey[300] : null,
borderRadius: BorderRadius.circular(16),
boxShadow: onPressed != null
? [
BoxShadow(
color: gradient.colors.first.withOpacity(0.3),
blurRadius: 12,
offset: const Offset(0, 6),
),
]
: [],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onPressed,
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 18),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
color: onPressed != null ? Colors.white : Colors.grey[500],
size: 24,
),
const SizedBox(width: 12),
Text(
label,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: onPressed != null ? Colors.white : Colors.grey[500],
),
),
],
),
),
),
),
);
}
Widget _buildOutlineButton({
required VoidCallback? onPressed,
required IconData icon,
required String label,
}) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: const Color(0xFFEF4444), width: 2),
borderRadius: BorderRadius.circular(16),
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onPressed,
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 18),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: const Color(0xFFEF4444), size: 24),
const SizedBox(width: 12),
Text(
label,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFFEF4444),
),
),
],
),
),
),
),
);
}
Widget _buildFeaturesList() {
final features = [
{'icon': Icons.speed_rounded, 'text': 'HTTP stream-based download'},
{'icon': Icons.notifications_active_rounded, 'text': 'Real-time progress'},
{'icon': Icons.check_circle_rounded, 'text': 'Download completion alert'},
{'icon': Icons.folder_rounded, 'text': 'Auto-save to Downloads'},
{'icon': Icons.phone_android_rounded, 'text': 'Android 13+ support'},
{'icon': Icons.phone_iphone_rounded, 'text': 'iOS notifications'},
{'icon': Icons.touch_app_rounded, 'text': 'Tap to open file'},
{'icon': Icons.cancel_rounded, 'text': 'Cancel anytime'},
];
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: const Color(0xFF3B82F6).withOpacity(0.08),
blurRadius: 20,
offset: const Offset(0, 4),
),
],
),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: const Color(0xFF3B82F6).withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.star_rounded,
color: Color(0xFF3B82F6),
size: 20,
),
),
const SizedBox(width: 12),
const Text(
'Features',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF1E293B),
),
),
],
),
const SizedBox(height: 20),
...features.map((feature) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFF3B82F6).withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
feature['icon'] as IconData,
color: const Color(0xFF3B82F6),
size: 18,
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
feature['text'] as String,
style: const TextStyle(
fontSize: 14,
color: Color(0xFF64748B),
),
),
),
],
),
)),
],
),
),
);
}
@override
void dispose() {
_urlController.dispose();
_filenameController.dispose();
_pulseController.dispose();
_successController.dispose();
super.dispose();
}
}