file_system_management
A comprehensive Flutter package for managing file transfers with progress tracking, queue management, social media-inspired UI widgets, and injectable handlers.
Features
| Feature | Description |
|---|---|
| Queue Management | Control concurrent transfers with priority queuing |
| Message Widgets | Ready-to-use widgets for chat applications |
| Social Media Themes | WhatsApp, Telegram, Instagram-inspired designs |
| Injectable Handlers | Use your own upload/download providers |
| Background Downloads | Continue downloads when app is in background |
| Caching | Automatic file caching with URL recognition |
| RTL Support | Full right-to-left language support |
| Progress Tracking | Real-time progress with speed and ETA |
Platform Support
| Android | iOS | Web | macOS | Windows | Linux |
|---|---|---|---|---|---|
| ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
Table of Contents
Installation
Add to your pubspec.yaml:
dependencies:
file_system_management: ^0.2.1
Then run:
flutter pub get
Getting Started
1. Initialize the Package
import 'package:file_system_management/file_system_management.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize directories
await AppDirectory.init();
// Initialize the file system controller
await FileSystemController.instance.initialize();
runApp(MyApp());
}
2. Apply Theme (Optional)
MaterialApp(
theme: ThemeData(
extensions: [SocialTransferThemeData.whatsapp()],
),
// ...
)
Usage Scenarios
Scenario 1: Basic File Download
Download a file with progress tracking and caching:
// Create a download task
final task = createDownloadTask(
url: 'https://example.com/document.pdf',
directory: 'downloads',
baseDirectory: BaseDirectory.applicationDocuments,
);
// Enqueue the download
final result = await FileSystemController.instance.enqueueDownload(task);
// Handle the result
switch (result) {
case EnqueueCached(:final filePath):
// File already exists in cache
openFile(filePath);
case EnqueueStarted(:final controller):
// Listen to progress
controller.stream.listen((item) {
print('Progress: ${item.progressText}');
print('Speed: ${item.networkSpeedText}');
print('ETA: ${item.timeRemainingText}');
if (item.isComplete) {
openFile(item.filePath);
}
});
case EnqueueInProgress(:final controller):
// Download already in progress, attach to existing stream
controller.stream.listen((item) => print(item.progressText));
case EnqueuePending(:final controller):
// Download queued, will start soon
print('Download pending...');
}
Scenario 2: Chat Application with Message Widgets
Build a messaging app with transfer widgets:
// Image message
ImageMessageTransferWidget(
url: 'https://example.com/photo.jpg',
fileName: 'photo.jpg',
fileSize: 2 * 1024 * 1024,
width: 250,
height: 180,
config: TransferWidgetConfig(
direction: BubbleDirection.incoming,
autoStart: false,
),
onDownload: (payload) async* {
// Your download implementation
yield* myDownloadService.download(payload.url);
},
onFullScreen: (path) => Navigator.push(
context,
MaterialPageRoute(builder: (_) => ImageViewer(path)),
),
)
// Audio message with waveform
AudioMessageTransferWidget(
url: 'https://example.com/voice.mp3',
fileName: 'voice_message.mp3',
duration: Duration(seconds: 30),
waveform: waveformData,
config: TransferWidgetConfig(
direction: BubbleDirection.outgoing,
),
onDownload: (payload) => downloadService.download(payload),
onPlay: () => audioPlayer.play(),
)
// Video message
VideoMessageTransferWidget(
url: 'https://example.com/video.mp4',
fileName: 'funny_video.mp4',
duration: Duration(minutes: 2, seconds: 30),
fileSize: 15 * 1024 * 1024,
config: TransferWidgetConfig(
direction: BubbleDirection.incoming,
),
onDownload: (payload) => downloadService.download(payload),
onPlay: (path) => openVideoPlayer(path),
)
// Document message
DocumentMessageTransferWidget(
url: 'https://example.com/report.pdf',
fileName: 'Annual_Report.pdf',
fileSize: 5 * 1024 * 1024,
pageCount: 25,
onDownload: (payload) => downloadService.download(payload),
onOpen: (path) => openPdfViewer(path),
)
// Generic file message
FileMessageTransferWidget(
url: 'https://example.com/archive.zip',
fileName: 'project_files.zip',
fileSize: 50 * 1024 * 1024,
onDownload: (payload) => downloadService.download(payload),
onOpen: (path) => shareFile(path),
)
Scenario 3: Queue Management for Multiple Downloads
Control concurrent downloads with priority queuing:
// Create a queue manager
final queue = DownloadQueueManager(
maxConcurrent: 3, // Only 3 downloads run simultaneously
autoRetry: true, // Retry failed downloads
maxRetries: 2, // Retry up to 2 times
);
// Add downloads with priority
queue.addUrl(
'https://example.com/urgent.pdf',
priority: TransferPriority.urgent, // Starts immediately
);
queue.addUrl(
'https://example.com/normal.pdf',
priority: TransferPriority.normal, // Queued normally
);
queue.addUrl(
'https://example.com/background.pdf',
priority: TransferPriority.low, // Downloads last
);
// Monitor queue state
queue.stateStream.listen((state) {
print('Running: ${state.runningCount}/${state.maxConcurrent}');
print('Pending: ${state.pendingCount}');
print('Progress: ${(state.overallProgress * 100).toStringAsFixed(1)}%');
});
// Queue control operations
queue.pause(); // Pause queue (running continues)
queue.start(); // Resume queue
queue.cancel('download_id'); // Cancel specific download
queue.cancelAll(); // Cancel all downloads
queue.retry('download_id'); // Retry failed download
queue.moveToFront('download_id'); // Move to front of queue
queue.changePriority('id', TransferPriority.urgent); // Change priority
queue.maxConcurrent = 5; // Adjust concurrency dynamically
// Wait for all downloads
final results = await queue.waitForAll();
Using Queue with Message Widgets
// Create a queued provider
final provider = QueuedTransferProvider(
maxConcurrent: 3,
downloadExecutor: (task, token) async* {
yield* myDownloadService.download(task.url, token);
},
);
// Or use with existing handler
final provider = QueuedTransferProvider.withHandler(
maxConcurrent: 3,
handler: MyDownloadHandler(),
);
// Use with widgets
ImageMessageTransferWidget(
url: 'https://example.com/image1.jpg',
onDownload: provider.createDownloadCallback(
priority: TransferPriority.high,
),
)
// Show queue status in UI
StreamBuilder<TransferQueueState>(
stream: provider.stateStream,
builder: (context, snapshot) {
final state = snapshot.data ?? provider.state;
return Row(
children: [
Text('${state.runningCount} running'),
Text('${state.pendingCount} queued'),
LinearProgressIndicator(value: state.overallProgress),
],
);
},
)
Scenario 4: Custom Download Handler
Implement your own download logic:
class MyDownloadHandler implements DownloadHandler {
final HttpClient _client;
MyDownloadHandler(this._client);
@override
Stream<TransferProgress> download(
DownloadPayload payload, {
TransferConfig? config,
CancellationToken? cancellationToken,
}) async* {
final request = await _client.getUrl(Uri.parse(payload.url));
// Add custom headers
if (payload.headers != null) {
payload.headers!.forEach((key, value) {
request.headers.add(key, value);
});
}
final response = await request.close();
final totalBytes = response.contentLength;
var bytesReceived = 0;
await for (final chunk in response) {
// Check for cancellation
if (cancellationToken?.isCancelled ?? false) {
yield TransferProgress(
status: TransferStatus.cancelled,
bytesTransferred: bytesReceived,
totalBytes: totalBytes,
);
return;
}
bytesReceived += chunk.length;
yield TransferProgress(
bytesTransferred: bytesReceived,
totalBytes: totalBytes,
status: TransferStatus.running,
);
}
yield TransferProgress.completed(totalBytes: totalBytes);
}
@override
Future<TransferResult> downloadAndComplete(
DownloadPayload payload, {
TransferConfig? config,
CancellationToken? cancellationToken,
}) async {
TransferProgress? lastProgress;
await for (final progress in download(
payload,
config: config,
cancellationToken: cancellationToken,
)) {
lastProgress = progress;
}
if (lastProgress?.isCompleted ?? false) {
return TransferSuccess(
localPath: payload.destinationPath ?? '',
remoteUrl: payload.url,
);
}
return TransferFailure(message: 'Download failed');
}
}
// Use the handler
ImageMessageTransferWidget(
url: 'https://example.com/image.jpg',
downloadHandler: MyDownloadHandler(HttpClient()),
)
Scenario 5: Social Media Theming
Apply pre-built or custom themes:
// WhatsApp theme
MaterialApp(
theme: ThemeData(
extensions: [SocialTransferThemeData.whatsapp()],
),
darkTheme: ThemeData(
brightness: Brightness.dark,
extensions: [SocialTransferThemeData.whatsapp(isDark: true)],
),
)
// Telegram theme
MaterialApp(
theme: ThemeData(
extensions: [SocialTransferThemeData.telegram()],
),
)
// Instagram theme
MaterialApp(
theme: ThemeData(
extensions: [SocialTransferThemeData.instagram()],
),
)
// Custom theme
MaterialApp(
theme: ThemeData(
extensions: [
SocialTransferThemeData(
primaryColor: Colors.purple,
bubbleColor: Colors.purple.shade50,
progressForegroundColor: Colors.purple,
successColor: Colors.green,
errorColor: Colors.red,
bubbleBorderRadius: BorderRadius.circular(20),
actionButtonSize: 52,
showSpeed: true,
showEta: true,
),
],
),
)
// Customize existing theme
SocialTransferThemeData.whatsapp().copyWith(
primaryColor: Colors.teal,
actionButtonSize: 56,
)
// Access theme in widget
final theme = context.socialTransferTheme;
// or
final theme = Theme.of(context).extension<SocialTransferThemeData>();
API Reference
Core Classes
| Class | Description |
|---|---|
FileSystemController |
Main controller for file operations |
TransferQueueManager<T> |
Generic queue manager for transfers |
DownloadQueueManager |
Specialized queue for downloads |
UploadQueueManager |
Specialized queue for uploads |
QueuedTransferProvider |
Provider for widget integration |
Transfer Types
| Class | Description |
|---|---|
TransferProgress |
Progress information (bytes, speed, ETA) |
TransferResult |
Sealed class: TransferSuccess, TransferFailure, TransferCancelled |
TransferStatus |
Enum: pending, running, paused, completed, failed, cancelled |
TransferPriority |
Enum: urgent, high, normal, low |
Message Widgets
| Widget | Use Case |
|---|---|
AudioMessageTransferWidget |
Voice messages, audio files |
ImageMessageTransferWidget |
Photos, images |
VideoMessageTransferWidget |
Videos |
FileMessageTransferWidget |
Generic files (ZIP, APK, etc.) |
DocumentMessageTransferWidget |
PDF, DOCX, XLSX, PPTX |
Handler Interfaces
| Interface | Description |
|---|---|
UploadHandler |
Implement for custom upload logic |
DownloadHandler |
Implement for custom download logic |
TransferHandler |
Combined upload + download handler |
Configuration
| Class | Description |
|---|---|
TransferWidgetConfig |
Widget behavior configuration |
TransferConfig |
Transfer operation configuration |
SocialTransferThemeData |
Theme configuration |
CancellationToken |
Transfer cancellation control |
TransferProgress Properties
| Property | Type | Description |
|---|---|---|
bytesTransferred |
int |
Bytes transferred so far |
totalBytes |
int |
Total bytes (-1 if unknown) |
progress |
double |
Progress ratio (0.0 - 1.0) |
progressPercent |
double |
Progress percentage (0 - 100) |
bytesPerSecond |
double |
Transfer speed |
estimatedTimeRemaining |
Duration? |
ETA |
status |
TransferStatus |
Current status |
progressText |
String |
Formatted: "5.2 MB / 10.0 MB" |
speedText |
String |
Formatted: "1.5 MB/s" |
etaText |
String |
Formatted: "2:30 remaining" |
TransferWidgetConfig Options
| Option | Type | Default | Description |
|---|---|---|---|
autoStart |
bool |
false |
Start transfer automatically |
showActionButton |
bool |
true |
Show download/upload button |
showProgress |
bool |
true |
Show progress percentage |
showSpeed |
bool |
true |
Show transfer speed |
showFileSize |
bool |
true |
Show file size |
showEta |
bool |
false |
Show estimated time |
allowPauseResume |
bool |
true |
Enable pause/resume |
allowRetry |
bool |
true |
Enable retry on failure |
allowCancel |
bool |
true |
Enable cancel |
direction |
BubbleDirection |
outgoing |
Message direction |
Example App
The package includes a comprehensive example app demonstrating:
- Chat Demo: Message widgets with different file types
- Queue Demo: Queue management with priority control
- Queued Chat Demo: WhatsApp-like interface with real downloads
- Settings: Theme switching, dark mode, RTL toggle
Run the example:
cd example
flutter run
Example: WhatsApp-like Chat with Real Downloads
class ChatScreen extends StatefulWidget {
@override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
late final RealDownloadProvider _provider;
@override
void initState() {
super.initState();
_provider = RealDownloadProvider(maxConcurrent: 3);
}
@override
void dispose() {
_provider.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Chat'),
actions: [
// Queue status indicator
StreamBuilder<TransferQueueState>(
stream: _provider.stateStream,
builder: (context, snapshot) {
final state = snapshot.data;
if (state == null || state.totalCount == 0) {
return SizedBox.shrink();
}
return Chip(
label: Text('${state.runningCount}/${state.pendingCount}'),
);
},
),
],
),
body: ListView.builder(
itemCount: messages.length,
itemBuilder: (context, index) {
final message = messages[index];
return _buildMessage(message);
},
),
);
}
Widget _buildMessage(Message message) {
switch (message.type) {
case MessageType.image:
return ImageMessageTransferWidget(
url: message.url,
fileName: message.fileName,
onDownload: (payload) => _provider.enqueueDownload(
url: payload.url,
expectedSize: payload.expectedSize,
),
);
case MessageType.video:
return VideoMessageTransferWidget(
url: message.url,
duration: message.duration,
onDownload: (payload) => _provider.enqueueDownload(
url: payload.url,
),
onPlay: (path) => _openVideoPlayer(path),
);
// ... other types
}
}
}
Migration Guide
From 0.1.x to 0.2.x
-
TaskItem renamed to TransferItem
// Old TaskItem item = ...; // New TransferItem item = ...; -
New message widgets
// Old: Manual widget creation // New: Use pre-built widgets AudioMessageTransferWidget(...) ImageMessageTransferWidget(...) VideoMessageTransferWidget(...) -
Theme system updated
// Old SocialTransferTheme(child: ...) // New ThemeData(extensions: [SocialTransferThemeData.whatsapp()]) -
Custom handlers
// New: Use onDownload/onUpload callbacks ImageMessageTransferWidget( onDownload: (payload) async* { ... }, onUpload: (payload) async* { ... }, )
Dependencies
| Package | Purpose |
|---|---|
background_downloader |
Background download support |
cached_network_image |
Network image caching |
path_provider |
System directories |
crypto |
Hash generation |
License
MIT License - see LICENSE for details.
Contributing
Contributions are welcome! Please read our contributing guidelines before submitting a pull request.
Support
Libraries
- file_system_management
- A Flutter package for managing file download and upload tasks with progress tracking, caching, and notifications.