transfer_kit 2.1.0+1 copy "transfer_kit: ^2.1.0+1" to clipboard
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 #

pub package License: MIT Flutter Firebase Platform Live Demo

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
PDF 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:

  1. Automatic Stream Sharing — No code changes needed, multiple widgets share streams
  2. Centralized Configuration — Use FileManagementConfig.init() to customize behavior
  3. New Exception Classes — Add error chaining with cause and stackTrace
  4. 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.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License #

This project is licensed under the MIT License - see the LICENSE file for details.


Made with Flutter

0
likes
100
points
5
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A comprehensive file management solution for Flutter with Firebase Storage - upload, download, progress tracking, intelligent caching, background transfers, and beautiful UI widgets.

Homepage

License

MIT (license)

Dependencies

awesome_notifications, background_downloader, cached_network_image, carousel_slider, cloud_firestore, collection, cross_file, crypto, dashed_circular_progress_bar, firebase_storage, flutter, flutter_animate, get_storage, image, intl, just_audio, just_waveform, logger, mime, open_filex, path, path_provider, pdfx, shared_preferences, uuid, video_thumbnail, workmanager

More

Packages that depend on transfer_kit