AI Edge Model DL (Downloader)
A Flutter plugin for downloading and managing AI models efficiently, with seamless integration for AI Edge packages. Simplifies model path management and provides resumable downloads, progress tracking, and automatic validation.
Features
- 🤝 AI Edge Integration - Seamless path resolution for AI Edge packages - just pass the model path directly
- 📥 Resumable Downloads - Automatically resume interrupted downloads from where they left off
- 📊 Progress Tracking - Real-time download progress with speed and time estimates
- ✅ Checksum Validation - Automatic MD5/SHA256 validation to ensure model integrity
- 🔄 Parallel Downloads - Concurrent chunk downloading for faster speeds
- 💾 Smart Storage - Platform-specific directory management with customizable paths
- 🎯 File Management - List, check, and delete downloaded models
- ⚡ Optimized Performance - Configurable chunk size and connection settings
Installation
flutter pub add ai_edge_model_dl
Or add it manually to your pubspec.yaml:
dependencies:
ai_edge_model_dl:
Getting Started
Basic Usage
import 'package:ai_edge_model_dl/ai_edge_model_dl.dart';
// Create a downloader instance
final downloader = ModelDownloader();
// Download a model
final result = await downloader.downloadModel(
Uri.parse('https://example.com/model.task'),
fileName: 'gemma.task',
expectedChecksum: 'abc123...', // Optional MD5 or SHA256
expectedFileSize: 1024000, // Optional size validation
onProgress: (progress) {
print('Progress: ${(progress.progress * 100).toStringAsFixed(1)}%');
print('Speed: ${progress.speed}');
print('Remaining: ${progress.remainingTime}');
},
);
print('Model downloaded to: ${result.filePath}');
print('Size: ${result.fileSize} bytes');
print('Checksum: ${result.checksum}');
Integration with AI Edge
import 'package:ai_edge/ai_edge.dart';
import 'package:ai_edge_model_dl/ai_edge_model_dl.dart';
// Download and use model with AI Edge
final downloader = ModelDownloader();
// Download model
final result = await downloader.downloadModel(
Uri.parse('https://example.com/gemma.task'),
fileName: 'gemma.task',
);
// Use directly with AI Edge - path resolution is handled automatically
final aiEdge = AiEdge.instance;
await aiEdge.initialize(
modelPath: result.filePath, // Direct path usage - no manual path management needed
maxTokens: 512,
);
// Or check if model exists before initializing
final modelPath = await downloader.getModelPath('gemma.task');
if (await downloader.isModelDownloaded('gemma.task')) {
await aiEdge.initialize(
modelPath: modelPath,
maxTokens: 512,
);
}
Configuration
// Create a downloader with custom configuration
final downloader = ModelDownloader(
config: const ModelDownloaderConfig(
// Custom base directory (default: platform-specific)
baseDirectory: '/custom/path',
// Subdirectory for models (default: 'models')
modelSubdirectory: 'ai_models',
// Download settings
chunkSize: 5 * 1024 * 1024, // 5MB chunks
maxConcurrentRequests: 4, // Parallel connections
maxRetries: 3, // Retry attempts
connectionTimeout: Duration(seconds: 30),
// Validation
checksumType: ChecksumType.sha256,
validationFailureAction: ValidationFailureAction.deleteAndError,
// Progress updates
progressInterval: Duration(milliseconds: 500),
// File conflict strategy
conflictStrategy: FileConflictStrategy.overwrite,
// Custom headers (e.g., for authentication)
headers: {'Authorization': 'Bearer token'},
),
);
Usage
Download with Progress Tracking
await downloader.downloadModel(
Uri.parse('https://huggingface.co/model.task'),
fileName: 'model.task',
onProgress: (progress) {
// Progress information
print('Downloaded: ${progress.downloadedSize} / ${progress.totalSize}');
print('Progress: ${(progress.progress * 100).toStringAsFixed(1)}%');
print('Speed: ${progress.speed}');
// Time estimates
print('Remaining time: ${progress.remainingTime}');
// Custom time formatting
final formatted = progress.formatRemainingTime('H hours M minutes');
print('Time left: $formatted');
},
);
Checksum Validation
// MD5 validation (32 characters)
final result = await downloader.downloadModel(
modelUri,
expectedChecksum: 'd41d8cd98f00b204e9800998ecf8427e',
);
// SHA256 validation (64 characters)
final result = await downloader.downloadModel(
modelUri,
expectedChecksum: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
);
// File size validation
final result = await downloader.downloadModel(
modelUri,
expectedFileSize: 1024000, // Expected size in bytes
);
Cancel Downloads
// Start a download
final downloadFuture = downloader.downloadModel(
modelUri,
onProgress: (progress) {
print('Progress: ${progress.progress}');
},
);
// Cancel if needed (uses CancelToken internally)
downloader.cancelDownload();
// Handle cancellation
try {
await downloadFuture;
} catch (e) {
print('Download cancelled or failed: $e');
}
Model Management
// Get models directory path
final modelsDir = await downloader.getModelsDirectory();
print('Models stored in: $modelsDir');
// Get path for a specific model
final modelPath = await downloader.getModelPath('gemma.task');
print('Model path: $modelPath');
// Check if a model exists
final exists = await downloader.isModelDownloaded('gemma.task');
print('Model exists: $exists');
// List all downloaded models
final models = await downloader.getDownloadedModels();
for (final model in models) {
print('Found model: $model');
}
// Delete a model
await downloader.deleteModel('old_model.task');
Error Handling
try {
final result = await downloader.downloadModel(
modelUri,
expectedChecksum: 'abc123...',
expectedFileSize: 1024000,
);
} on ModelDownloaderException catch (e) {
switch (e.code) {
case ModelDownloaderErrorCode.checksumMismatch:
print('Checksum validation failed: ${e.message}');
case ModelDownloaderErrorCode.fileSizeMismatch:
print('File size validation failed: ${e.message}');
case ModelDownloaderErrorCode.invalidChecksumFormat:
print('Invalid checksum format: ${e.message}');
}
} catch (e) {
// Handle other exceptions (network errors, etc.)
print('Download failed: $e');
}
Resume Interrupted Downloads
Downloads are automatically resumed if interrupted. The downloader:
- Detects partial downloads (
.tmpfiles) - Verifies existing chunks
- Continues from the last successful position
- Validates the complete file after resuming
// If this download is interrupted...
await downloader.downloadModel(
modelUri,
fileName: 'large_model.task',
);
// ...calling it again will resume from where it stopped
await downloader.downloadModel(
modelUri,
fileName: 'large_model.task',
);
Platform Setup
iOS
No additional setup required. Models are stored in the app's Application Support directory.
Android
No additional setup required. Models are stored in the app's external storage directory (if available) or Application Support directory.
Storage Locations
By default, models are stored in:
- iOS:
<app_support>/models/(Application Support directory - not backed up to iCloud) - Android:
<external_storage>/models/or<app_support>/models/(External storage preferred for better space availability)
You can customize the storage location:
final downloader = ModelDownloader(
config: const ModelDownloaderConfig(
baseDirectory: '/custom/path',
modelSubdirectory: 'ai_models',
),
);
API Reference
Main Classes
ModelDownloader
The main class for downloading and managing models.
ModelDownloaderConfig
Configuration options:
baseDirectory: Custom base directory path (nullable)modelSubdirectory: Subdirectory name for models (default: 'models')headers: HTTP headers for requestschecksumType: Type of checksum validation (ChecksumType.md5,ChecksumType.sha256,ChecksumType.none)conflictStrategy: How to handle existing files (FileConflictStrategy.overwrite,FileConflictStrategy.rename,FileConflictStrategy.error)progressInterval: How often to emit progress updatesconnectionTimeout: HTTP connection timeoutmaxConcurrentRequests: Number of parallel connectionschunkSize: Size of each download chunk in bytesmaxRetries: Number of retry attemptstempFileExtension: Extension for temporary files during download (default: '.tmp')validationFailureAction: Action on validation failure (ValidationFailureAction.deleteAndError,ValidationFailureAction.keepAndError)
ModelDownloadProgress
Download progress information:
progress: Download percentage (0.0 to 1.0)downloadedBytes: Bytes downloaded so fartotalBytes: Total file sizebytesPerSecond: Current download speedestimatedTimeRemaining: Estimated time to completiondownloadedSize: Human-readable downloaded size (e.g., "1.5 MB")totalSize: Human-readable total sizespeed: Human-readable speed (e.g., "2.5 MB/s")remainingTime: Formatted remaining timeformatRemainingTime(pattern): Custom time formatting
ValidationFailureAction
Actions when validation fails:
deleteAndError: Delete the file and throw error (default)keepAndError: Keep the file and throw error
ModelDownloaderException
Exception class for model downloader errors:
code: Error code (ModelDownloaderErrorCode)message: Human-readable error messagedetails: Optional details map with error-specific information
ModelDownloaderErrorCode
Error codes for exception handling:
checksumMismatch: Checksum validation failedfileSizeMismatch: File size validation failedinvalidChecksumFormat: Invalid checksum format (wrong length)
Example App
Check out the complete example in the repository for demonstrations of:
- Model downloading with progress UI
- Resume capability after app restart
- Download cancellation
- Error handling
- Model management (list, delete)
Troubleshooting
Common Issues
Download fails immediately:
- Check network connectivity
- Verify the URL is accessible
- Ensure proper headers are set if authentication is required
Checksum validation fails:
- Verify the expected checksum is correct
- Ensure the checksum type matches (MD5 vs SHA256)
- Check if the file is corrupted during download
Out of storage space:
- Check available device storage
- Clean up old models using
deleteModel() - Consider downloading to external storage on Android
Slow download speeds:
- Increase
chunkSizefor better throughput - Adjust
maxConcurrentRequestsbased on server capabilities - Check network connection quality
License
This project is licensed under the BSD 3-Clause License - see the LICENSE file for details.