tus_background_upload
TUS resumable uploads that keep running while your app is backgrounded or the device is locked — including uploads that take longer than Android's ~9-minute background cap.
Pure-Dart TUS protocol layer on top of background_downloader's native transports (iOS background URLSession, Android WorkManager with foreground-service promotion). No native code of its own.
Features
TusUploader— one state machine per file: confirmed-offset tracking, whole-file progress (a resumed upload continues at the fraction the user last saw), and pause / resume / cancel:pause()stops the transfer but keeps the server-side offset;resume(...)re-reads the offset (TUSHEAD) and continues from there;cancel()completes theupload(...)future withTusUploadCancelledException.
createTusUpload(...)— the TUS creation extension (POSTwithUpload-Length/Upload-Metadata→Location), for servers like tusd that don't hand out upload URLs some other way.configureBackgroundUploads(...)— one call at app start that lifts the platform limits: AndroidrunInForeground(foreground service + notification, so WorkManager doesn't kill the task after ~9 minutes) and a configurable iOSresourceTimeout(default 8 h).BackgroundMultipartUploader— plain multipart uploads (images, documents) over the same background-safe native transports.TusTransport— the wire seam; inject a fake in tests, or swap the transport entirely.
Quick start
import 'package:tus_background_upload/tus_background_upload.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Once at app start — enables long-running background transfers.
await configureBackgroundUploads(
notificationTitle: 'Uploading',
notificationBody: 'Your file keeps uploading in the background',
);
runApp(const MyApp());
}
Future<void> uploadVideo(File file) async {
// Android 13+ needs the notification permission for the >9-minute guarantee.
await requestBackgroundUploadNotificationPermission();
// 1. TUS creation — skip if your backend already hands out upload URLs.
final Uri uploadUrl = await createTusUpload(
creationEndpoint: Uri.parse('https://tusd.tusdemo.net/files/'),
fileLength: await file.length(),
metadata: <String, String>{'filename': 'video.mp4'},
);
// 2. The transfer — runs natively, survives backgrounding and device lock.
final TusUploader uploader = TusUploader(transport: BackgroundDownloaderTusTransport());
await uploader.upload(
endpoint: uploadUrl,
headers: const <String, String>{},
filePath: file.path,
fileLength: await file.length(),
onProgress: (double progress) => print('${(progress * 100).toStringAsFixed(0)}%'),
);
}
Pause and resume:
await uploader.pause(); // server keeps the received bytes
await uploader.resume( // HEADs the offset, PATCHes the rest
endpoint: uploadUrl,
headers: const <String, String>{},
filePath: file.path,
fileLength: fileLength,
onProgress: onProgress,
);
Platform setup
Android
For uploads that must outlive the ~9-minute background window, add to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
and request the notification permission at runtime (requestBackgroundUploadNotificationPermission()). Without the grant, uploads still run but Android may stop them after ~9 minutes in the background.
iOS
Nothing to declare — transfers run on a background URLSession out of the box. configureBackgroundUploads sets the resource timeout (default 8 h) so very large files aren't abandoned early.
Example
The example/ app picks a file and uploads it to any TUS server (default: the public tusd demo server) with a progress bar and pause / resume / cancel buttons — handy for verifying the background behavior on a real device: start a big upload, background the app for 10+ minutes, watch it finish.
Testing your own code
TusUploader takes any TusTransport, so unit tests inject a fake transport and drive offsets/progress by hand — see test/tus_uploader_test.dart.