background_transfer 1.1.1
background_transfer: ^1.1.1 copied to clipboard
A Flutter plugin for handling background file transfers (uploads and downloads) with progress tracking and notifications support.
Background Transfer Plugin #
A Flutter plugin for handling background file transfers (uploads and downloads) with progress tracking and notifications support. Works on both iOS and Android platforms.
Features #
- Background file downloads with progress tracking
- Background file uploads with progress tracking
- Support for any file type (images, videos, documents, etc.)
- Progress notifications on both platforms
- Multipart form data upload support
- Custom headers support
- Automatic MIME type detection
- Concurrent transfer support
- Transfer cancellation support
- Lifecycle-aware progress tracking
Getting Started #
Add this to your package's pubspec.yaml file:
dependencies:
background_transfer: ^1.0.0
Usage #
Initialize the plugin #
final transfer = getBackgroundTransfer();
Download a file #
try {
final taskId = await transfer.startDownload(
fileUrl: 'https://example.com/file.pdf',
savePath: '/path/to/save/file.pdf',
headers: {
'Authorization': 'Bearer token',
},
);
// Listen to download progress
transfer.getDownloadProgress(taskId).listen(
(progress) {
print('Download progress: ${(progress * 100).toStringAsFixed(1)}%');
},
onDone: () {
print('Download completed!');
},
onError: (error) {
print('Download failed: $error');
},
);
} catch (e) {
print('Failed to start download: $e');
}
Upload a file #
try {
final taskId = await transfer.startUpload(
filePath: '/path/to/file.pdf',
uploadUrl: 'https://example.com/upload',
headers: {
'Authorization': 'Bearer token',
},
fields: {
'title': 'My Document',
'type': 'pdf',
},
);
// Listen to upload progress
transfer.getUploadProgress(taskId).listen(
(progress) {
print('Upload progress: ${(progress * 100).toStringAsFixed(1)}%');
},
onDone: () {
print('Upload completed!');
},
onError: (error) {
print('Upload failed: $error');
},
);
} catch (e) {
print('Failed to start upload: $e');
}
Cancel a transfer #
final success = await transfer.cancelTask(taskId);
Check transfer completion #
final isComplete = await transfer.isUploadComplete(taskId);
// or
final isComplete = await transfer.isDownloadComplete(taskId);
Platform Support #
Android | iOS |
---|---|
✅ | ✅ |
Android Setup #
Add the following permissions to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
For Android 13 (API level 33) and above, you'll also need to request runtime permissions:
// Request notification permission for Android 13+
if (Platform.isAndroid) {
final status = await Permission.notification.request();
print('Notification permission status: $status');
}
iOS Setup #
- Add the following keys to your Info.plist file:
<!-- Background download/upload support -->
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
<string>remote-notification</string>
</array>
<!-- Notification support -->
<key>UNUserNotificationCenter</key>
<string>YES</string>
<key>NSUserNotificationAlertStyle</key>
<string>banner</string>
<!-- Privacy descriptions -->
<key>NSPhotoLibraryUsageDescription</key>
<string>Access to photo library is required for uploading images</string>
<key>NSDocumentsFolderUsageDescription</key>
<string>Access to documents is required for file transfers</string>
- For iOS 15 and above, to enable background download/upload capabilities, add this to your AppDelegate:
if #available(iOS 15.0, *) {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.yourapp.transfer",
using: nil
) { task in
// Handle background task
task.setTaskCompleted(success: true)
}
}
Advanced Usage: Implementing Queue Management #
The plugin provides basic transfer capabilities but does not include queue management. Here's a suggested implementation using BLoC pattern to add queuing in your application:
// transfer_task.dart
class TransferTask {
final String id;
final String path; // filePath for upload, savePath for download
final String url; // uploadUrl for upload, fileUrl for download
final Map<String, String> headers;
final Map<String, String> fields; // Only for upload
final String? taskId;
final bool isUpload; // true for upload, false for download
TransferTask({
required this.id,
required this.path,
required this.url,
required this.headers,
required this.isUpload,
this.fields = const {},
this.taskId,
});
// Add fromJson/toJson methods...
}
// transfer_bloc.dart
class TransferBloc extends HydratedBloc<TransferEvent, TransferState> {
final FileTransferHandler transfer;
StreamSubscription<double>? _progressSub;
TransferBloc(this.transfer) : super(const TransferState()) {
on<AddTransferTask>(_onAddTransferTask);
on<StartNextTransfer>(_onStartNextTransfer);
on<TransferCompleted>(_onTransferCompleted);
// Add other event handlers...
}
// Implementation details...
}
Using the Transfer Queue #
First, wrap your app with BlocProvider to make the TransferBloc available throughout your widget tree:
void main() {
runApp(
MultiBlocProvider(
providers: [
BlocProvider<TransferBloc>(
create: (context) => TransferBloc(getBackgroundTransfer()),
),
],
child: MyApp(),
),
);
}
Then you can access the bloc from any widget:
// Get the bloc instance
final transferBloc = context.read<TransferBloc>();
// Add a download task
transferBloc.add(AddTransferTask(
TransferTask(
id: 'unique_id',
path: '/path/to/save/file.pdf',
url: 'https://example.com/file.pdf',
headers: {'Authorization': 'Bearer token'},
isUpload: false,
),
));
// Add an upload task
transferBloc.add(AddTransferTask(
TransferTask(
id: 'unique_id',
path: '/path/to/file.pdf',
url: 'https://example.com/upload',
headers: {'Authorization': 'Bearer token'},
fields: {'title': 'My Document'},
isUpload: true,
),
));
// Listen to state changes
StreamBuilder<TransferState>(
stream: transferBloc.stream,
builder: (context, snapshot) {
// Build your UI based on the state
return // Your widget tree...
},
);
This queue implementation example provides:
- One transfer at a time to prevent bandwidth competition
- Persistent task queue across app restarts (using HydratedBloc)
- Proper error handling and retry mechanisms
- Clean cancellation and resume functionality
- Progress tracking for the active transfer
Note: This is just one way to implement queuing. You can adapt this example or create your own implementation based on your specific needs.
Notes #
- Files are downloaded and uploaded in the background, allowing transfers to continue even when the app is in the background
- Progress notifications are shown on both platforms
- The plugin automatically handles lifecycle changes and restores progress tracking when the app is resumed
- Concurrent transfers are supported and tracked independently
- MIME types are automatically detected based on file extensions
Upcoming Features #
Future versions will include:
- Native queue management implementation templates
- iOS: Example implementation using NSURLSession with built-in queue management
- Android: Example implementation using WorkManager with transfer queue
- This will allow developers to implement queue management directly in their apps without depending on the plugin's queue system
- Advanced retry strategies with exponential backoff
- Bandwidth throttling options
- Transfer prioritization
- Network type restrictions (WiFi only, etc.)
- More granular progress reporting
Note: While the plugin uses NSURLSession (iOS) and WorkManager (Android) for background transfers, it does not include queue management. The upcoming feature will provide native example implementations to help developers implement queue management in their preferred way, either at the Dart level (as shown in the Advanced Usage section) or directly in native code.