Background task manager for Flutter — native workers, task chains, zero Flutter Engine overhead.
Schedule background tasks that survive app restarts, reboots, and force-quits. Native Kotlin/Swift workers handle I/O without spawning the Flutter Engine.
| workmanager | native_workmanager | |
|---|---|---|
| HTTP/file tasks without Flutter Engine | ❌ | ✅ |
| Task chains (A → B → C) | ❌ | ✅ |
| 11 built-in workers | ❌ | ✅ |
| Constraints enforced (network, charging…) | ✅ | ✅ fixed in v1.0.5 |
| Periodic tasks that actually repeat | ✅ | ✅ fixed in v1.0.5 |
| Dart callbacks for custom logic | ✅ | ✅ |
| Custom Kotlin/Swift workers (no fork) | ❌ | ✅ |
Requirements: Android API 26+ · iOS 14.0+
flutter pub add native_workmanagervoid main() async {
WidgetsFlutterBinding.ensureInitialized();
await NativeWorkManager.initialize();
runApp(MyApp());
}Periodic sync — no Flutter Engine:
await NativeWorkManager.enqueue(
taskId: 'hourly-sync',
trigger: TaskTrigger.periodic(Duration(hours: 1)),
worker: NativeWorker.httpSync(url: 'https://api.example.com/sync'),
constraints: Constraints(requiresNetwork: true),
);Custom Dart logic:
await NativeWorkManager.enqueue(
taskId: 'process-data',
trigger: TaskTrigger.oneTime(initialDelay: Duration(minutes: 5)),
worker: DartWorker(callbackId: 'processData'),
constraints: Constraints(requiresNetwork: true, requiresCharging: true),
);| Worker | What it does |
|---|---|
httpSync |
Fire-and-forget API call |
httpDownload |
Download with resume + checksum |
httpUpload |
Single or multi-file upload with progress |
httpRequest |
Full HTTP request with response validation |
fileCompress |
ZIP a folder or file list |
fileDecompress |
Extract ZIP (zip-slip + zip-bomb protected) |
fileSystem |
Copy, move, delete, list, mkdir |
imageProcess |
Resize/compress/convert, EXIF-aware |
cryptoEncrypt |
AES-256-GCM encrypt (random salt, PBKDF2) |
cryptoDecrypt |
AES-256-GCM decrypt |
hashFile |
MD5, SHA-1, SHA-256, SHA-512 |
Extend with your own Kotlin or Swift workers — no forking, no MethodChannel boilerplate. Runs on native thread, zero Flutter Engine overhead.
// Android — implement AndroidWorker
class EncryptWorker : AndroidWorker {
override suspend fun doWork(input: String?): WorkerResult {
val path = Json.parseToJsonElement(input!!).jsonObject["path"]!!.jsonPrimitive.content
// Android Keystore, Room, TensorFlow Lite — any native API
return WorkerResult.Success()
}
}
// Register in MainActivity.kt (once):
// SimpleAndroidWorkerFactory.setUserFactory { name -> if (name == "EncryptWorker") EncryptWorker() else null }// iOS — implement IosWorker
class EncryptWorker: IosWorker {
func doWork(input: String?) async throws -> WorkerResult {
// CryptoKit, Core Data, Core ML — any native API
return .success()
}
}
// Register in AppDelegate.swift (once):
// IosWorkerFactory.registerWorker(className: "EncryptWorker") { EncryptWorker() }// Dart — identical call on both platforms
await NativeWorkManager.enqueue(
taskId: 'encrypt-file',
trigger: TaskTrigger.oneTime(),
worker: NativeWorker.custom(
className: 'EncryptWorker',
input: {'path': '/data/document.pdf'},
),
);await NativeWorkManager.beginWith(
TaskRequest(
id: 'download',
worker: NativeWorker.httpDownload(url: 'https://cdn.example.com/model.zip', savePath: '/tmp/model.zip'),
),
).then(
TaskRequest(
id: 'extract',
worker: NativeWorker.fileDecompress(zipPath: '/tmp/model.zip', targetDir: '/data/model/', deleteAfterExtract: true),
),
).then(
TaskRequest(
id: 'verify',
worker: NativeWorker.hashFile(filePath: '/data/model/weights.bin', algorithm: HashAlgorithm.sha256),
),
).enqueue(chainName: 'update-model');
// Pure native — zero Flutter Engine, each step retries independentlyNativeWorkManager.events.listen((e) => print('${e.taskId}: ${e.success ? "done" : e.message}'));
NativeWorkManager.progress.listen((u) => print('${u.taskId}: ${u.progress}% — ${u.bytesDownloaded}B'));Before:
Workmanager().registerPeriodicTask('sync', 'apiSync', frequency: Duration(hours: 1));After:
NativeWorkManager.enqueue(
taskId: 'sync',
trigger: TaskTrigger.periodic(Duration(hours: 1)),
worker: NativeWorker.httpSync(url: 'https://api.example.com/sync'),
);~90% API compatible. Migration guide → · Migration CLI →
- Periodic tasks repeat correctly — trigger type was hardcoded to OneTime (fixed)
- Constraints enforced —
requiresNetwork,initialDelay, etc. were silently ignored on Android (fixed) - Chain resume preserves worker config — all config was lost after app kill (fixed)
- Custom iOS worker registration —
IosWorkerprotocol is nowpublic(fixed) - HttpDownload resume — partial downloads preserved on error so retries use
Rangeheader (fixed) - Swift Package Manager support — works with both SPM and CocoaPods
Quick Start · API Reference · FAQ · Android Setup · iOS Background Limits · Migration Guide · Security
Use cases: Periodic Sync · File Upload · Photo Backup · Chain Processing · Custom Workers
MIT License · Author: Nguyễn Tuấn Việt · BrewKits
If this saves you time, a ⭐ on GitHub helps a lot.