Skip to content

brewkits/native_workmanager

Repository files navigation

native_workmanager

native_workmanager

Background tasks for Flutter that run in pure Kotlin & Swift — no Flutter Engine boot, no 50 MB RAM hit, no 2-second cold-start penalty.

pub.dev Pub Points CI MIT Android 8.0+ iOS 14.0+


Quick Start

1. Add the dependency:

dependencies:
  native_workmanager: ^1.1.0

2. Initialize once in main():

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await NativeWorkManager.initialize();
  runApp(MyApp());
}

3. Schedule your first background task:

await NativeWorkManager.enqueue(
  taskId: 'daily-sync',
  worker: NativeWorker.httpSync(url: 'https://api.example.com/sync'),
  constraints: Constraints(requiresWifi: true),
);

iOS only — run once to configure BGTaskScheduler in your Xcode project automatically:

dart run native_workmanager:setup_ios

Why native_workmanager?

The popular workmanager plugin boots a full Flutter Engine for every background task — ~50 MB RAM, up to 3 seconds startup, and a process that the OS kills aggressively on battery-constrained devices.

native_workmanager skips the engine entirely. Workers run as pure Kotlin coroutines or Swift async tasks.

Metric workmanager (Dart-based) native_workmanager
Memory per task ~50–100 MB ~2–5 MB
Task startup 1,500–3,000 ms < 50 ms
Battery impact High Ultra-low
Survives OS task kill ❌ Engine crash ✅ Native resilience
Custom Dart workers ✅ (opt-in via DartWorker)

Platform Support

Feature Android iOS
One-time tasks
Periodic tasks ✅ (BGAppRefresh)
Task chains
Constraints (Wi-Fi, charging, storage)
Foreground service (long tasks)
Custom Dart workers
Min OS version Android 8.0 (API 26) iOS 14.0

Built-in Native Workers

25+ production-grade workers, zero engine overhead:

Category Workers
HTTP / Network httpDownload (resumable), httpUpload (multipart), parallelDownload (chunked), httpSync, httpRequest
Media imageResize, imageCrop, imageConvert, imageThumbnail (all EXIF-aware)
PDF pdfMerge, pdfCompress, imagesToPdf
Crypto cryptoEncrypt (AES-256-GCM), cryptoDecrypt, cryptoHash (SHA-256/512), hmacSign
File System fileCopy, fileMove, fileDelete, fileCompress (ZIP), fileDecompress, fileList
Storage moveToSharedStorage (Android MediaStore / iOS Files)
Real-time webSocket (connect / send / receive) — Android

Secure Task Chains

Chain workers into persistent pipelines. Each step only runs when the previous one succeeds. Data flows automatically between steps.

await NativeWorkManager
  .beginWith(TaskRequest(
    id: 'download',
    worker: NativeWorker.httpDownload(
      url: 'https://cdn.example.com/photo.jpg',
      savePath: '/tmp/raw.jpg',
    ),
  ))
  .then(TaskRequest(
    id: 'resize',
    worker: NativeWorker.imageResize(
      inputPath: '/tmp/raw.jpg',
      outputPath: '/tmp/thumb.jpg',
      maxWidth: 512,
    ),
  ))
  .then(TaskRequest(
    id: 'upload',
    worker: NativeWorker.httpUpload(
      url: 'https://api.example.com/photos',
      filePath: '/tmp/thumb.jpg',
    ),
  ))
  .named('photo-pipeline')
  .enqueue();
  • Persistent — survives device reboots and app kills (SQLite-backed state)
  • Per-step retry — Step 2 retries independently; Step 1 never re-runs
  • Parallel steps — use .thenAll([...]) to run tasks concurrently then join

Custom Dart Workers

Need app-specific logic? Register a Dart function as a background worker:

@pragma('vm:entry-point')
Future<bool> myWorker(Map<String, dynamic> input) async {
  final userId = input['userId'] as String;
  await syncUserData(userId);
  return true;
}

// Register once at startup
NativeWorkManager.registerDartWorker('user-sync', myWorker);

// Schedule it
await NativeWorkManager.enqueue(
  taskId: 'sync-user-42',
  worker: DartWorker(workerName: 'user-sync', input: {'userId': '42'}),
);

Listen to Task Events

NativeWorkManager.events.listen((event) {
  if (event.isStarted) return; // lifecycle event, not a result
  if (event.success) {
    print('✅ ${event.taskId} completed');
    print('   result: ${event.resultData}');
  } else {
    print('❌ ${event.taskId} failed: ${event.message}');
  }
});

Common Use Cases

📸 Photo Backup Pipeline
await NativeWorkManager
  .beginWith(TaskRequest(
    id: 'fetch',
    worker: NativeWorker.httpDownload(url: photoUrl, savePath: '/tmp/photo.jpg'),
  ))
  .then(TaskRequest(
    id: 'compress',
    worker: NativeWorker.imageResize(
      inputPath: '/tmp/photo.jpg',
      outputPath: '/tmp/photo_compressed.jpg',
      maxWidth: 1920,
      quality: 85,
    ),
  ))
  .then(TaskRequest(
    id: 'upload',
    worker: NativeWorker.httpUpload(
      url: 'https://backup.example.com/upload',
      filePath: '/tmp/photo_compressed.jpg',
    ),
  ))
  .named('photo-backup')
  .enqueue();
🔐 Encrypt & Upload Sensitive File
await NativeWorkManager
  .beginWith(TaskRequest(
    id: 'encrypt',
    worker: NativeWorker.cryptoEncrypt(
      inputPath: '/documents/report.pdf',
      outputPath: '/tmp/report.enc',
      password: securePassword,
    ),
  ))
  .then(TaskRequest(
    id: 'upload',
    worker: NativeWorker.httpUpload(
      url: 'https://vault.example.com/store',
      filePath: '/tmp/report.enc',
    ),
  ))
  .named('secure-backup')
  .enqueue();
⏱ Periodic Background Sync
await NativeWorkManager.enqueue(
  taskId: 'hourly-sync',
  worker: NativeWorker.httpSync(url: 'https://api.example.com/sync'),
  trigger: TaskTrigger.periodic(intervalMinutes: 60),
  constraints: Constraints(requiresNetworkConnectivity: true),
  policy: ExistingTaskPolicy.keep,
);

Documentation

Guide Description
Getting Started Full setup walkthrough with copy-paste examples
API Reference Complete reference for all public types
Migration from workmanager Switch in under 5 minutes
iOS Setup Guide BGTaskScheduler configuration details
Architecture How zero-engine execution works

Support


MIT License · Made by BrewKits

If native_workmanager saves you time, a ⭐ on GitHub goes a long way.