hybrid_task_runner 1.1.0 copy "hybrid_task_runner: ^1.1.0" to clipboard
hybrid_task_runner: ^1.1.0 copied to clipboard

A Flutter package implementing a Hybrid Background Strategy using AlarmManager for precision scheduling and WorkManager for reliable long-running task execution on Android.

Hybrid Task Runner #

A Flutter package for running background tasks on Android. It combines AlarmManager + WorkManager to get the best of both worlds.

Platform: Android only (iOS is not supported yet)

Why Was This Made? #

Background execution on Android is complicated. There are two main options:

  1. AlarmManager - High precision, can set exact times, but only runs for ~10 seconds
  2. WorkManager - Can run for a long time (10+ minutes), but timing is not exact as it's batched by the system

This package combines both:

  • AlarmManager triggers at the precise time
  • Immediately enqueues a WorkManager task
  • WorkManager runs the heavy task (can run 10+ minutes)
  • After completion, schedules the next alarm

So you get timing precision AND long execution duration.

Dependencies #

dependencies:
  android_alarm_manager_plus: ^4.0.0
  workmanager: ^0.9.0
  shared_preferences: ^2.0.0

Why use these?

Package Purpose
android_alarm_manager_plus Set exact alarm, wake up device, survive reboot
workmanager Execute long-running task, survive process death
shared_preferences Store callback handle & config across isolates

Install #

dependencies:
  hybrid_task_runner:
    git:
      url: https://github.com/user/hybrid_task_runner.git

Android Setup #

1. Edit android/app/src/main/AndroidManifest.xml #

Add permissions inside <manifest>:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.USE_EXACT_ALARM"/>

Add service & receivers inside <application>:

<!-- AlarmManager -->
<service
    android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:exported="false"/>
<receiver
    android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
    android:exported="false"/>
<receiver
    android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>

2. Ensure minSdkVersion >= 21 #

In android/app/build.gradle:

android {
    defaultConfig {
        minSdkVersion 21
    }
}

Usage #

1. Create the callback function #

IMPORTANT: Must be a top-level function (outside of any class) with the @pragma('vm:entry-point') annotation.

@pragma('vm:entry-point')
Future<bool> myBackgroundTask() async {
  // Do heavy work here
  // Can run for 10+ minutes
  
  await syncDataToServer();
  await processLocalFiles();
  
  return true; // return false if failed
}

Why must it be top-level? Because the callback runs in a separate isolate, not in your app's main isolate.

2. Initialize in main() #

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

3. Start the runner #

await HybridRunner.start(
  callback: myBackgroundTask,
  loopInterval: Duration(minutes: 15),
  runImmediately: true,
  taskOverlapPolicy: TaskOverlapPolicy.parallel, // optional
);

Task Overlap Policy

Controls what happens when a new task is triggered while a previous task is still running:

Policy Behavior
TaskOverlapPolicy.replace Cancel the running task, start the new one (default)
TaskOverlapPolicy.skipIfRunning Ignore the new task if one is already running
TaskOverlapPolicy.parallel Run both tasks simultaneously, no waiting

Example use cases:

  • replace: When you only care about the latest data sync
  • skipIfRunning: When tasks should never overlap (e.g., database operations)
  • parallel: When each task is independent (e.g., processing different files)

4. Stop if needed #

await HybridRunner.stop();

5. Check status #

bool isRunning = await HybridRunner.isActive;
Duration? interval = await HybridRunner.loopInterval;

Multi-Task API #

For more complex scenarios, you can register multiple named tasks with independent schedules.

Register looping tasks #

// Task 1: Sync data every 15 minutes
await HybridRunner.registerTask(
  name: 'syncData',
  callback: syncDataTask,
  interval: Duration(minutes: 15),
  taskOverlapPolicy: TaskOverlapPolicy.skipIfRunning,
);

// Task 2: Process files every 30 minutes
await HybridRunner.registerTask(
  name: 'processFiles',
  callback: processFilesTask,
  interval: Duration(minutes: 30),
  taskOverlapPolicy: TaskOverlapPolicy.parallel,
);

Register one-time tasks #

One-time tasks run once and are automatically removed after execution.

// Run once after 30 minutes
await HybridRunner.registerTask(
  name: 'sendReminder',
  callback: sendReminderTask,
  interval: Duration(minutes: 30),
  isOneTime: true, // Runs once, then removed
);

// Run immediately (1 second delay)
await HybridRunner.registerTask(
  name: 'initialSync',
  callback: initialSyncTask,
  interval: Duration(minutes: 1),
  isOneTime: true,
  runImmediately: true,
);

View all registered tasks #

final tasks = await HybridRunner.getRegisteredTasks();
for (final task in tasks) {
  print('Task: ${task.name}');
  print('  Interval: ${task.interval.inMinutes} minutes');
  print('  Active: ${task.isActive}');
  print('  One-time: ${task.isOneTime}');
  print('  Registered: ${task.registeredAt}');
}

Stop a specific task #

final stopped = await HybridRunner.stopTask('syncData');
print('Task stopped: $stopped'); // true if found and stopped

Stop all tasks #

await HybridRunner.stopAllTasks();

How It Works #

User starts runner
       ↓
Schedule AlarmManager (exact time)
       ↓
[Device sleep / App closed]
       ↓
AlarmManager fires! (max 10 sec)
       ↓
Enqueue WorkManager task
       ↓
WorkManager executes callback (max 10+ min)
       ↓
Schedule next alarm
       ↓
[Loop continues...]

Limitations #

Force Close #

If the user force closes the app (from Settings > Apps > Force Stop), all alarms are cancelled. This is Android behavior, not a bug.

Workaround: This package also registers a WorkManager periodic task as backup. After ~15 minutes, the task will run again and re-schedule the alarm.

Battery Optimization #

Some vendors (Xiaomi, Oppo, Vivo, Samsung) have aggressive battery optimization that can kill background processes. Users need to whitelist the app in settings.

No Minimum Interval #

Unlike standalone WorkManager periodic tasks (which have a 15-minute minimum), the hybrid approach has no minimum interval. AlarmManager can trigger at any time, and WorkManager one-off tasks run immediately when enqueued.

The 15-minute minimum only applies to the backup periodic task - a fallback that runs if alarms are cancelled (e.g., after force close). This backup will re-schedule the alarm when it runs.

Tips #

  1. Test on a real device - Emulator is sometimes not accurate for background behavior
  2. Don't force close - Just swipe away or press back
  3. Whitelist from battery optimization - Important for reliability
  4. Short intervals work - Unlike pure WorkManager, you can use intervals < 15 minutes

API Reference #

Single-Task API (Simple) #

Method Description
HybridRunner.initialize() Initialize AlarmManager and WorkManager. Call once in main().
HybridRunner.start({...}) Start a single task loop with the given callback and interval.
HybridRunner.stop() Stop the single task loop.
HybridRunner.isActive Check if the runner is currently active.
HybridRunner.loopInterval Get the current loop interval.

Multi-Task API (Advanced) #

Method Description
HybridRunner.registerTask({...}) Register a named task (looping or one-time).
HybridRunner.getRegisteredTasks() Get a list of all registered tasks.
HybridRunner.stopTask(name) Stop and remove a specific task by name.
HybridRunner.stopAllTasks() Stop all registered tasks.

registerTask Parameters

Parameter Type Default Description
name String required Unique task identifier
callback Function required Top-level async function
interval Duration required Interval (loop) or delay (one-time)
taskOverlapPolicy TaskOverlapPolicy replace Overlap behavior
runImmediately bool false Start immediately
isOneTime bool false Run once then auto-remove

Classes & Enums #

Type Description
TaskOverlapPolicy Enum: replace, skipIfRunning, parallel
RegisteredTask Task model with: name, interval, isActive, isOneTime, registeredAt

License #

MIT

3
likes
0
points
121
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter package implementing a Hybrid Background Strategy using AlarmManager for precision scheduling and WorkManager for reliable long-running task execution on Android.

Repository (GitHub)
View/report issues

Topics

#background #android #alarm #workmanager #task

License

unknown (license)

Dependencies

android_alarm_manager_plus, flutter, shared_preferences, workmanager

More

Packages that depend on hybrid_task_runner