transfer_kit 2.1.0+1
transfer_kit: ^2.1.0+1 copied to clipboard
A comprehensive file management solution for Flutter with Firebase Storage - upload, download, progress tracking, intelligent caching, background transfers, and beautiful UI widgets.
TransferKit #
Production-ready file transfer solution for Flutter with Firebase Storage, smart caching, and stream sharing.
FileLoadingCard(
url: 'https://firebasestorage.googleapis.com/.../image.jpg',
onLoaded: (file) => Image.file(file),
)
Overview #
TransferKit provides a complete solution for handling file uploads and downloads in Flutter applications. Built with performance and developer experience in mind, it offers intelligent caching, progress tracking, task management, and beautiful pre-built UI components.
Why TransferKit? #
| Feature | Benefit |
|---|---|
| Stream Sharing | Multiple widgets requesting the same file share a single stream, reducing memory and network overhead |
| Smart Caching | Automatic file caching prevents redundant downloads |
| Task Persistence | Transfer state survives app restarts |
| Pre-built Widgets | Production-ready UI components for common use cases |
| Type Safety | Full Dart null-safety support with comprehensive type definitions |
Features #
- File Upload & Download — Full Firebase Storage integration with real-time progress tracking
- Stream Sharing — Optimized resource usage when multiple widgets request the same file
- Smart Caching — Automatic file caching to avoid redundant downloads
- Task Management — Pause, resume, cancel, and retry file operations
- Batch Operations — Upload/download multiple files in parallel or sequentially
- Persistent State — Task state persists across app restarts
- Background Transfers — Continue transfers when app is in background
- Rich UI Components — Beautiful, customizable widgets for file operations
- Progress Tracking — Real-time progress updates with transfer speed and ETA
- Error Handling — Comprehensive exception handling with error chaining
Quick Start #
Installation #
Add to your pubspec.yaml:
dependencies:
transfer_kit: ^2.1.0
Basic Setup #
Customize the library behavior with FileManagementConfig:
import 'package:transfer_kit/transfer_kit.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Configure before initialization
await FileManagementConfig.init(
maxConcurrentDownloads: 3,
maxConcurrentUploads: 2,
streamCleanupDelay: Duration(seconds: 5),
enableLogging: kDebugMode,
cacheEnabled: true,
maxCacheSize: 1024 * 1024 * 1024, // 1 GB
);
runApp(MyApp());
}
Download a File #
// Using the widget (recommended)
FileLoadingCard(
url: 'https://firebasestorage.googleapis.com/.../image.jpg',
onLoaded: (file) => Image.file(file),
)
// Using the API directly
final fileManager = FileManagementSystem();
final task = await fileManager.downloadTask(
filePathAndUrl: FilePathAndURL.url(url: imageUrl),
taskId: 'download_001',
);
Upload a File #
// Using the widget
FileUploadCard(
filePath: '/path/to/file.jpg',
destinationPath: 'uploads/file.jpg',
onUploaded: (task) => Text('Uploaded: ${task.downloadUrl}'),
)
// Using the API directly
final task = await fileManager.uploadTask(
filePathAndUrl: FilePathAndURL.local(
path: '/path/to/file.jpg',
destinationPath: 'uploads/file.jpg',
),
taskId: 'upload_001',
group: FileGroupInfo(id: 'my_uploads'),
);
Architecture #
Stream Sharing Pattern #
When multiple widgets request the same file, the library automatically shares a single stream:
Widget A ─┐
Widget B ─┼──► Single Firebase Task ──► Shared Broadcast Stream
Widget C ─┘ │ │
▼ ▼
One snapshotEvents All widgets receive
listener same updates
This architecture provides:
- Reduced Memory Usage — One listener per unique transfer
- Consistent State — All widgets see the same progress
- Automatic Cleanup — Resources freed when all subscribers disconnect
Core API #
FileManagementSystem #
The main controller for all file operations.
final fileManager = FileManagementSystem();
// Download operations
Future<FileTask> downloadTask({...});
Stream<FileTask> downloadTaskStream({...});
Stream<MultiDownloadFileTask> downloadTasksParallelStream({...});
// Upload operations
Future<FileTask> uploadTask({...});
Stream<FileTask> uploadTaskStream({...});
Stream<MultiUploadFileTask> uploadTasksParallelStream({...});
// Task control
Future<bool> startTask(String taskId);
Future<bool> pauseTask(String taskId);
Future<bool> resumeTask(String taskId);
Future<bool> cancelTask(String taskId);
Future<bool> retryTask(String taskId);
// Queries
FileTask? getTaskById(String taskId);
Set<FileTask> getTasksByGroupId(String groupId);
Stream<FileTask?> getTaskStreamById(String taskId);
FileTask #
Represents a file transfer operation.
class FileTask {
final String id;
final String? filePath;
final String? downloadUrl;
final FileTaskState state;
final FileTaskType type;
final FileProgress progress;
// Computed properties
bool get isComplete;
bool get isRunning;
bool get isPaused;
double get progressPercentage;
int get bytesTransferred;
int get totalBytes;
}
Task States #
enum FileTaskState {
waiting, // Queued but not started
running, // Actively transferring
paused, // Paused by user
completed, // Successfully completed
error, // Encountered an error
cancelled, // Cancelled by user
cached, // Served from cache
}
Widgets #
FileLoadingCard #
Downloads and displays a file with progress indication.
FileLoadingCard(
url: 'https://example.com/image.jpg',
onLoaded: (file) => Image.file(file, fit: BoxFit.cover),
downloadingWidget: (context, task) => CircularProgressIndicator(
value: task?.progressPercentage ?? 0 / 100,
),
onError: (error) => Icon(Icons.error),
checkCacheFirst: true,
)
FileUploadCard #
Uploads a file with progress indication.
FileUploadCard(
filePath: '/path/to/file.pdf',
destinationPath: 'documents/report.pdf',
onUploaded: (task) => Column(
children: [
Icon(Icons.check_circle, color: Colors.green),
Text('Upload complete!'),
SelectableText(task.downloadUrl!),
],
),
uploadingWidget: (context, task) => LinearProgressIndicator(
value: task?.progressPercentage ?? 0 / 100,
),
)
MultiFileLoadingCard #
Downloads multiple files with combined progress.
MultiFileLoadingCard(
urls: {'url1', 'url2', 'url3'},
isSequential: false, // Download in parallel
onLoaded: (files) => GridView.builder(
itemCount: files.length,
itemBuilder: (_, i) => Image.file(files[i]),
),
onFileLoaded: (file, index) => print('File $index downloaded'),
onAllFilesLoaded: (files) => print('All ${files.length} files ready'),
)
MultiFileUploadCard #
Uploads multiple files with combined progress.
MultiFileUploadCard(
filePathsAndUrls: {
FilePathAndURL(path: '/file1.jpg', destinationPath: 'uploads/1.jpg'),
FilePathAndURL(path: '/file2.jpg', destinationPath: 'uploads/2.jpg'),
},
onUploaded: (downloadUrls) => Text('Uploaded ${downloadUrls.length} files'),
onFileUploaded: (url, index) => print('File $index: $url'),
)
Media Widgets #
Pre-built widgets for common media types:
// Image with automatic caching
DownloadImageWidget(
file: FileModel(url: 'https://example.com/photo.jpg'),
fit: BoxFit.cover,
onTap: (context, filePath) => openImageViewer(filePath),
)
// Video with thumbnail preview
DownloadVideoWidget(
file: FileModel(
url: 'https://example.com/video.mp4',
thumbnail: thumbnailBytes,
durationInSeconds: 120,
),
onTap: (context, filePath) => playVideo(filePath),
)
// Image carousel
DownloadImageSliderWidget(
imageFiles: [file1, file2, file3],
height: 300,
autoStart: true,
)
Advanced Usage #
Batch Operations with Progress #
final groupId = 'batch_${DateTime.now().millisecondsSinceEpoch}';
// Monitor batch progress
fileManager.streamTasksBy(groupId: groupId).listen((tasks) {
final completed = tasks.where((t) => t.isComplete).length;
final total = tasks.length;
final totalBytes = tasks.fold<int>(0, (sum, t) => sum + t.totalBytes);
final transferred = tasks.fold<int>(0, (sum, t) => sum + t.bytesTransferred);
print('Progress: $completed/$total files');
print('Transferred: ${transferred.formatBytes} / ${totalBytes.formatBytes}');
});
Custom Error Handling #
try {
await fileManager.downloadTask(...);
} on FileDownloadException catch (e) {
print('Download failed: ${e.message}');
print('Cause: ${e.cause}');
print('Stack trace: ${e.stackTrace}');
} on FileCacheException catch (e) {
print('Cache error: ${e.message}');
}
Stream Statistics #
Monitor the efficiency of stream sharing:
final stats = FirebaseStorageFactory.getActiveStreamStats();
print('Active download streams: ${stats['downloadStreams']}');
print('Active upload streams: ${stats['uploadStreams']}');
print('Total download subscribers: ${stats['downloadSubscribers']}');
print('Total upload subscribers: ${stats['uploadSubscribers']}');
Cache Management #
// Check if file is cached
final isCached = await fileManager.isFileCached(url);
// Get cached file path
final cachedPath = await fileManager.getCachedFilePath(url);
// Clear specific cache
await fileManager.removeCachedFile(url);
// Clear all cache
await fileManager.clearCache();
Configuration #
FileManagementConfig #
The library provides a centralized configuration system for customizing behavior.
Available Settings
| Setting | Type | Default | Description |
|---|---|---|---|
maxConcurrentDownloads |
int |
5 | Maximum simultaneous downloads |
maxConcurrentUploads |
int |
3 | Maximum simultaneous uploads |
streamCleanupDelay |
Duration |
3 seconds | Delay before cleaning unused streams |
defaultAutoStart |
bool |
true | Auto-start transfers by default |
enableLogging |
bool |
false | Enable debug logging |
retryAttempts |
int |
3 | Number of retry attempts on failure |
retryDelay |
Duration |
2 seconds | Delay between retry attempts |
cacheEnabled |
bool |
true | Enable file caching |
maxCacheSize |
int |
500 MB | Maximum cache size in bytes |
cacheExpiration |
Duration |
7 days | How long to keep cached files |
Metadata Extraction Settings
| Setting | Type | Default | Description |
|---|---|---|---|
autoExtractMetadata |
bool |
true | Enable automatic metadata extraction |
autoExtractSha256 |
bool |
false | Compute SHA-256 hash of files |
autoExtractThumbnail |
bool |
false | Generate thumbnails for images/videos/PDFs |
autoExtractWaveform |
bool |
false | Generate waveform data for audio files |
thumbnailMaxWidth |
int |
200 | Maximum thumbnail width in pixels |
thumbnailMaxHeight |
int |
200 | Maximum thumbnail height in pixels |
waveformSamplesPerSecond |
int |
30 | Waveform resolution (samples per second) |
Initialize Configuration
// Initialize with custom settings
FileManagementConfig.init(
// Transfer settings
maxConcurrentDownloads: 3,
maxConcurrentUploads: 2,
streamCleanupDelay: Duration(seconds: 5),
enableLogging: true,
retryAttempts: 5,
// Cache settings
cacheEnabled: true,
maxCacheSize: 1024 * 1024 * 1024, // 1 GB
cacheExpiration: Duration(days: 30),
// Metadata extraction settings
autoExtractMetadata: true,
autoExtractSha256: true,
autoExtractThumbnail: true,
autoExtractWaveform: true,
thumbnailMaxWidth: 300,
thumbnailMaxHeight: 300,
waveformSamplesPerSecond: 50,
);
Access Configuration at Runtime
// Get current configuration
final config = FileManagementConfig.instance;
print('Max downloads: ${config.maxConcurrentDownloads}');
print('Cache enabled: ${config.cacheEnabled}');
// Update settings at runtime
config.setMaxConcurrentDownloads(5);
config.setLoggingEnabled(false);
config.setCacheEnabled(true);
// Get all settings as map (useful for debugging)
print(config.toMap());
Reset Configuration
// Reset to default values
FileManagementConfig.reset();
Media Metadata #
The library provides comprehensive metadata extraction and storage for files.
MediaMetadata Class #
Stores detailed metadata for images, videos, audio files, and documents:
final metadata = MediaMetadata(
// Common properties
mimeType: 'image/jpeg',
fileSize: 1024000,
fileName: 'photo.jpg',
// Image/Video dimensions
width: 1920,
height: 1080,
// Video/Audio duration
durationInSeconds: 120.5,
// Document properties
pageCount: 10,
title: 'Annual Report',
author: 'John Doe',
);
Using Metadata with Downloads #
Pass metadata from your API response when creating download tasks:
// Download with metadata from API
final task = await fileManager.downloadTask(
filePathAndUrl: FilePathAndURL.url(
url: 'https://firebasestorage.googleapis.com/.../image.jpg',
metadata: MediaMetadata(
mimeType: 'image/jpeg',
width: 1920,
height: 1080,
fileSize: apiResponse['size'],
),
),
taskId: 'download_001',
);
Automatic Metadata Extraction #
After download/upload completion, metadata is automatically extracted from the local file and merged with any existing metadata:
// After download completes, access full metadata
final cachedFile = FilePathAndURLRepository.instance.getByUrl(url);
final metadata = cachedFile?.metadata;
print('Type: ${metadata?.mimeType}'); // image/jpeg
print('Size: ${metadata?.fileSize}'); // 1024000
print('Dimensions: ${metadata?.width}x${metadata?.height}'); // 1920x1080
print('Duration: ${metadata?.duration}'); // For video/audio
print('SHA-256: ${metadata?.sha256}'); // If enabled
print('Page Count: ${metadata?.pageCount}'); // For PDF
Supported File Types #
| Type | Extracted Data | Package Used |
|---|---|---|
| Images | Dimensions, EXIF, orientation, alpha, thumbnails | image |
| Videos | Thumbnails | video_thumbnail |
| Audio | Duration, waveform visualization | just_audio, just_waveform |
| Page count, first page thumbnail | pdfx |
|
| All Files | MIME type, SHA-256, size, timestamps | mime, crypto |
Metadata Sources #
Track where metadata came from:
enum MetadataSource {
api, // From backend API response
firebase, // From Firebase Storage metadata
cache, // From local cache
local, // Extracted from local file
}
Waveform Data (Audio) #
Automatic waveform extraction for audio visualization:
// Enable waveform extraction in config
FileManagementConfig.init(
autoExtractWaveform: true,
waveformSamplesPerSecond: 30, // Samples per second of audio
);
// After audio download, waveform is available
final metadata = cachedFile?.metadata;
final waveform = metadata?.waveform;
print('Samples: ${waveform?.samples.length}'); // e.g., 900 for 30s audio
print('Peak amplitude: ${waveform?.peakAmplitude}'); // 0.0 to 1.0
print('Channels: ${waveform?.channels}'); // 1 (mono) or 2 (stereo)
// Use samples for visualization
CustomPaint(
painter: WaveformPainter(samples: waveform?.samples ?? []),
)
Thumbnail Data #
Automatic thumbnail extraction for images, videos, and PDF documents:
// Enable thumbnail extraction in config
FileManagementConfig.init(
autoExtractThumbnail: true,
thumbnailMaxWidth: 200,
thumbnailMaxHeight: 200,
);
// After download, thumbnail is available for:
// - Images (resized version)
// - Videos (first frame)
// - PDFs (first page rendered as image)
final thumbnail = metadata?.thumbnail;
// Display thumbnail
if (thumbnail?.bytes != null) {
Image.memory(thumbnail!.bytes!);
}
// Or use base64 for persistence
final base64 = thumbnail?.base64;
Exception Classes #
The library provides typed exceptions for better error handling:
| Exception | Description |
|---|---|
FileException |
Base class for all file exceptions |
FileDownloadException |
Download operation failed |
FileUploadException |
Upload operation failed |
FileDeleteException |
File deletion failed |
FileCacheException |
Cache operation failed |
FileTaskException |
Task operation (pause/resume/cancel) failed |
All exceptions support error chaining:
class FileException implements Exception {
final String message;
final Object? cause; // Original exception
final StackTrace? stackTrace;
}
Extension Methods #
Useful extensions included with the package:
// File path extensions
'path/to/file.pdf'.fileName // 'file.pdf'
'path/to/file.pdf'.extension // 'pdf'
'some-url'.toHashName() // Hashed filename for caching
// Number formatting
1500000.formatBytes // '1.5 MB'
// String utilities
jsonString.toListMap() // Parse JSON to List<Map>
'https://...'.isURL // true
Dependencies #
Core Dependencies #
dependencies:
flutter:
sdk: flutter
firebase_storage: ^12.0.0
cloud_firestore: ^5.0.0
get_storage: ^2.1.1
path_provider: ^2.1.0
collection: ^1.18.0
logger: ^2.0.0
Metadata Extraction (Optional) #
# File identification
crypto: ^3.0.0 # SHA-256 hash computation
mime: ^1.0.0 # MIME type detection
# Media metadata
image: ^4.0.0 # Image dimensions, EXIF, thumbnails
video_thumbnail: ^0.5.0 # Video thumbnail extraction
just_audio: ^0.9.0 # Audio duration
just_waveform: ^0.0.7 # Audio waveform generation
pdfx: ^2.0.0 # PDF page count and thumbnails
UI Widgets #
cached_network_image: ^3.4.1
carousel_slider: ^5.0.0 # Image carousel
dashed_circular_progress_bar: ^0.0.6
Migration Guide #
From v1.x to v2.x #
The v2.x release of TransferKit introduces stream sharing and centralized configuration. Existing code continues to work without changes, but you can now benefit from:
- Automatic Stream Sharing — No code changes needed, multiple widgets share streams
- Centralized Configuration — Use
FileManagementConfig.init()to customize behavior - New Exception Classes — Add error chaining with
causeandstackTrace - Stream Statistics — Monitor resource usage with
getActiveStreamStats()
New Features
// New: Centralized configuration
FileManagementConfig.init(
maxConcurrentDownloads: 3,
enableLogging: true,
);
// New: Exception chaining
try {
await download();
} on FileDownloadException catch (e) {
print('Cause: ${e.cause}');
}
// New: Stream statistics
final stats = FirebaseStorageFactory.getActiveStreamStats();
Contributing #
Contributions are welcome! Please read our contributing guidelines before submitting pull requests.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Made with Flutter