smartdownloader 1.0.2
smartdownloader: ^1.0.2 copied to clipboard
A Flutter plugin for smart file downloading with progress, pause, resume, retry, and background support.
SmartDownloader #
smartdownloader is a Flutter package for reliable file downloads with support for direct URL downloads and automatic API response parsing.
This package now supports both:
- Existing flows (
DynamicDownloadManager, dashboard UI, retry callbacks) - New upgrade APIs (
SmartDownloader.downloadFromResponse,SmartDownloader.sync)
Features #
- Backward-compatible existing APIs remain available
- Direct download API (
SmartDownloader.download) - Response-based download API (
SmartDownloader.downloadFromResponse) - Auto-detect mode (
SmartDownloader.sync) - Parse
Map,List, nested maps, nested arrays, and mixed JSON - Wildcard collection paths like
data.adSequences[*][*] - Validate URLs before starting downloads
- Deduplicate URLs before download
- Configurable concurrency (
maxConcurrent, default3) - Resume partial downloads using
.partfiles - Restore unfinished tasks after app restart
- Skip duplicate downloads when file already exists
- Detect storage failures and fail gracefully
- Retry failed downloads with network recovery behavior
- Optional UI widgets:
DynamicDownloadDashboard(existing)SmartDownloaderWidget(new, optional)
Installation #
dependencies:
smartdownloader: ^1.0.0
Import #
import 'package:smartdownloader/smartdownloader.dart';
Backward Compatibility (Still Works) #
All existing usage remains valid.
await SmartDownloader.download(
url: 'https://example.com/file.mp4',
fileName: 'file.mp4',
customDownloadDir: '/my/path',
);
Existing DynamicDownloadManager APIs are still available:
final updatedJson = await DynamicDownloadManager.processResponse(
jsonResponse: myJsonPayload,
extractKeys: const ['store_image_url', 'adUrl'],
customDownloadDir: '/my/path',
);
New API: downloadFromResponse #
Use resolvers to map collection path + field names.
final items = await SmartDownloader.downloadFromResponse(
response,
customDownloadDir: '/my/path',
collections: const [
{
'path': 'data.adSequences[*][*]',
'url': 'adUrl',
'title': 'name',
'id': 'uuid',
'type': 'mediaType',
'size': 'contentLength',
'folder': 'ads',
},
{
'path': 'data.businessCategories[*]',
'url': 'categoryImageUrl',
'title': 'name',
'id': 'uuid',
'folder': 'categories',
},
],
maxConcurrent: 3,
onItemUpdate: (item) {
// item.status / item.progress updates
},
onTaskProgress: (url, progress, totalBytes) {},
);
New API: sync (Auto-Detect Mode) #
final items = await SmartDownloader.sync(
response,
customDownloadDir: '/my/path',
maxConcurrent: 3,
);
Auto-detected URL keys:
urlfileUrladUrlimageUrlcategoryImageUrldownloadUrl
Auto-detected metadata keys:
- title/name:
name,title - id:
uuid,id
Optional UI (New) #
SmartDownloaderWidget(
items: items,
onRetry: (item) {
// Custom retry behavior
},
)
Existing Dashboard UI (Still Works) #
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:smartdownloader/smartdownloader.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: DemoWrapper());
}
}
class DemoWrapper extends StatefulWidget {
const DemoWrapper({super.key});
@override
State<DemoWrapper> createState() => _DemoWrapperState();
}
class _DemoWrapperState extends State<DemoWrapper> {
String? _downloadDir;
final String myApiResponse = '''
{
"data": [
{
"id": 1,
"store_image_url": "https://picsum.photos/id/237/200/300",
"adUrl": "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/360/Big_Buck_Bunny_360_10s_1MB.mp4"
}
]
}
''';
@override
void initState() {
super.initState();
_initPath();
}
Future<void> _initPath() async {
final dir = await getApplicationDocumentsDirectory();
setState(() => _downloadDir = '${dir.path}/my_downloads');
}
@override
Widget build(BuildContext context) {
if (_downloadDir == null) {
return const Scaffold(body: Center(child: CircularProgressIndicator()));
}
return Scaffold(
body: DynamicDownloadDashboard(
jsonResponse: jsonDecode(myApiResponse),
extractKeys: const ['store_image_url', 'adUrl'],
customDownloadDir: _downloadDir!,
initialIsGridView: true,
initialThemeMode: ThemeMode.dark,
),
);
}
}
Reliability Behavior #
- Internet lost mid-download: task moves to waiting state and continues when online
- App closed/killed: task metadata is persisted and reused on next launch
- Storage full: operation fails with graceful status (no corrupted final file)
- Partial/corrupted downloads: writes to
.partfirst, renames only after success - No duplicates: if target file already exists, download is skipped as completed
Notes #
- Resume depends on server support for HTTP
Range; unsupported servers fall back to restart. - URL validation uses
HEADwith fallbackGETfor servers that blockHEAD. - Best results are for JSON-like API responses (
Map/List/nested JSON). For non-JSON payloads, use directdownload(...).