AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Flutter Background Tasks

skill-iml1s-flutter-claude-skills-flutter-background-tasks · by ImL1s

Flutter background task configuration guide for iOS (background_fetch/BGTaskScheduler) and Android (WorkManager). Use when setting up periodic background tasks, debugging background execution failures, or auditing platform configuration for background work.

No reviews yet
0 installs
25 views
0.0% view→install

Install

$ agentstack add skill-iml1s-flutter-claude-skills-flutter-background-tasks

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-iml1s-flutter-claude-skills-flutter-background-tasks)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
5mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Flutter Background Tasks? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Flutter Background Tasks (iOS + Android)

Complete guide for configuring periodic background tasks in Flutter apps using workmanager (Android) and background_fetch (iOS).

Package Versions

dependencies:
  workmanager: ^0.9.0          # Android WorkManager wrapper
  background_fetch: ^1.5.0     # iOS BGTaskScheduler wrapper

Architecture Overview

Foreground App                     Background Isolate
┌──────────────────┐               ┌──────────────────┐
│ RevenueCat SDK   │               │ SharedPreferences │
│ SubscriptionProv │──writes──►    │ (cached status)   │
│                  │  lobster_     │                   │
│                  │  subscription_│──reads──► Gate     │
│                  │  cached       │         .isSubscribed()
└──────────────────┘               └──────────────────┘

Background isolates CANNOT use:
- MethodChannel-based SDKs (RevenueCat, Firebase Auth listeners)
- EventChannel streams
- Platform views

Background isolates CAN use:
- SharedPreferences (read/write)
- HTTP requests (Dio, http)
- Firebase Core, Firestore, AppCheck (after re-init)
- Local file I/O

iOS Setup (background_fetch)

1. Info.plist (CRITICAL)

BGTaskSchedulerPermittedIdentifiers

    
    com.transistorsoft.fetch
    
    com.transistorsoft.customtask

UIBackgroundModes

    fetch        
    processing   

Common mistake: Forgetting com.transistorsoft.fetch causes BGAppRefreshTask to silently fail with no error logs. This is the #1 cause of iOS background tasks not executing.

2. Xcode Capabilities

In Xcode: Target > Signing & Capabilities > Background Modes:

  • [x] Background fetch
  • [x] Background processing

3. AppDelegate.swift

No changes needed for iOS 13+. The SDK auto-registers from Info.plist.

If using workmanager for OTHER tasks (not background_fetch):

// Only for workmanager tasks, NOT background_fetch
WorkmanagerPlugin.registerPeriodicTask(
    withIdentifier: "com.example.myTask",
    frequency: NSNumber(value: 24 * 60 * 60)
)
WorkmanagerPlugin.setPluginRegistrantCallback { registry in
    GeneratedPluginRegistrant.register(with: registry)
}

4. Podfile

The use_frameworks! line should work for most projects. If you encounter linking errors:

# Try static linkage if dynamic causes issues
use_frameworks! :linkage => :static

Warning: Changing linkage can break other SDKs (Appodeal, AdMob). Test thoroughly.

5. iOS Constraints & Limitations

| Constraint | Value | |-----------|-------| | Minimum interval | 15 minutes (iOS enforces, ignores higher values) | | Background time limit | ~30 seconds | | Scheduling control | Apple's ML algorithm decides when to run | | After reboot | Automatically re-registered from Info.plist | | App terminated | Works if enableHeadless: true | | Must call finish | BackgroundFetch.finish(taskId) or task gets throttled | | Custom taskId prefix | Must start with com.transistorsoft. |

6. iOS Timeout Handling

Always implement timeout with 2-second margin:

// iOS background time limit is ~30 seconds
// Use 28 seconds to leave margin for cleanup
final timeout = isIOSBackground ? 28 : 540; // seconds

await Future.any([
  _doWork(),
  Future.delayed(Duration(seconds: timeout)).then((_) {
    throw TimeoutException('Background task timed out');
  }),
]);

Android Setup (WorkManager)

1. AndroidManifest.xml


    
    
    
    
    ...

2. build.gradle.kts

android {
    compileOptions {
        // Required for WorkManager + Kotlin
        isCoreLibraryDesugaringEnabled = true
    }
}

dependencies {
    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}

3. ProGuard (if using R8 minification)

WorkManager's AAR includes consumer ProGuard rules automatically. Usually no explicit rules needed. If you see reflection errors in release builds:

# Only if needed
-keep class androidx.work.** { *; }
-dontwarn androidx.work.**

4. Android Constraints & Limitations

| Constraint | Value | |-----------|-------| | Minimum interval | 15 minutes (WorkManager enforces) | | Background time limit | ~10 minutes | | Network constraint | NetworkType.connected recommended | | After reboot | Automatic (requires RECEIVEBOOTCOMPLETED) | | App terminated | Always works (WorkManager is OS-level) | | Battery optimization | May be delayed by Doze mode | | Backoff | Exponential recommended for network tasks |

Dart Implementation

Entry Point Functions

CRITICAL: Both callback functions MUST have @pragma('vm:entry-point') to prevent tree-shaking in release builds.

// Android WorkManager callback
@pragma('vm:entry-point')
void myCallbackDispatcher() {
  Workmanager().executeTask((taskName, inputData) async {
    // Re-initialize plugins in background isolate
    WidgetsFlutterBinding.ensureInitialized();
    await Firebase.initializeApp();

    // Do work...
    return true; // success
  });
}

// iOS background_fetch headless callback
@pragma('vm:entry-point')
void myHeadlessTask(HeadlessTask task) async {
  final taskId = task.taskId;
  final isTimeout = task.timeout;

  if (isTimeout) {
    BackgroundFetch.finish(taskId);
    return;
  }

  // Re-initialize plugins
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // Do work...

  BackgroundFetch.finish(taskId); // MUST call or task gets throttled
}

Scheduler Pattern

class BackgroundScheduler {
  final String taskName;
  final String uniqueId;

  Future register({
    required int intervalHours,
    required Function callbackDispatcher,
    Function(String)? iosCallback,
    Function(HeadlessTask)? headlessTask,
  }) async {
    if (Platform.isAndroid) {
      await Workmanager().initialize(callbackDispatcher);
      await Workmanager().registerPeriodicTask(
        uniqueId,
        taskName,
        frequency: Duration(hours: intervalHours),
        constraints: Constraints(networkType: NetworkType.connected),
        existingWorkPolicy: ExistingPeriodicWorkPolicy.replace,
        backoffPolicy: BackoffPolicy.exponential,
        initialDelay: const Duration(minutes: 1),
      );
    } else if (Platform.isIOS) {
      await BackgroundFetch.configure(
        BackgroundFetchConfig(
          minimumFetchInterval: 15, // iOS minimum
          stopOnTerminate: false,
          enableHeadless: true,
        ),
        iosCallback ?? (_) {},
        (taskId) => BackgroundFetch.finish(taskId), // timeout handler
      );
      if (headlessTask != null) {
        BackgroundFetch.registerHeadlessTask(headlessTask);
      }
    }
  }
}

Subscription Gate Pattern (Background-Safe)

/// Reads cached subscription status from SharedPreferences.
/// Does NOT call any MethodChannel-based SDK.
class RevenueCatGate implements SubscriptionGate {
  final AgentRepository _repo;

  RevenueCatGate(this._repo);

  @override
  Future isSubscribed() async {
    return _repo.getCachedSubscriptionStatus();
    // Reads: prefs.getBool('${keyPrefix}_subscription_cached')
  }
}

Foreground writes (in SubscriptionProvider):

// Write to SharedPreferences when subscription state changes
await prefs.setBool('${keyPrefix}_subscription_cached', isSubscribed);

Key consistency: The foreground write key and background read key MUST match exactly. Use the same keyPrefix pattern.

Debugging Background Tasks

iOS Simulator

Force-trigger background fetch:

# In Xcode: Debug > Simulate Background Fetch
# Or via command line:
xcrun simctl background_fetch  

Android

# List WorkManager tasks
adb shell dumpsys jobscheduler | grep 

# Force run immediately (for testing)
adb shell cmd jobscheduler run -f  

Common Issues

| Symptom | Cause | Fix | |---------|-------|-----| | iOS: Tasks never execute | Missing com.transistorsoft.fetch in Info.plist | Add to BGTaskSchedulerPermittedIdentifiers | | iOS: Tasks stop after a while | Not calling BackgroundFetch.finish() | Always call finish, even on timeout | | iOS: Works in debug, not release | Missing @pragma('vm:entry-point') | Add pragma to both callback functions | | Android: Tasks don't survive reboot | Missing RECEIVEBOOTCOMPLETED | Add permission to AndroidManifest.xml | | Android: Release build crash | R8 removing Worker classes | Add ProGuard keep rules | | Both: SDK calls fail in background | Using MethodChannel SDK in isolate | Cache state in SharedPreferences, read in background | | Both: Subscription check fails | Cache key mismatch foreground/background | Verify keyPrefix produces same SharedPreferences key |

Audit Checklist

When reviewing a Flutter background task setup, verify:

iOS

  • [ ] com.transistorsoft.fetch in BGTaskSchedulerPermittedIdentifiers
  • [ ] fetch in UIBackgroundModes
  • [ ] processing in UIBackgroundModes (if using scheduleTask)
  • [ ] Background Modes capability enabled in Xcode
  • [ ] @pragma('vm:entry-point') on headless callback
  • [ ] BackgroundFetch.finish(taskId) called in ALL paths (success + timeout)
  • [ ] Timeout < 30 seconds (recommend 28s)
  • [ ] No MethodChannel SDK calls in background isolate

Android

  • [ ] RECEIVE_BOOT_COMPLETED permission in AndroidManifest.xml
  • [ ] Core library desugaring enabled in build.gradle
  • [ ] @pragma('vm:entry-point') on callbackDispatcher
  • [ ] NetworkType.connected constraint for network tasks
  • [ ] Exponential backoff configured
  • [ ] Firebase re-initialized in background isolate
  • [ ] ProGuard rules if R8 minification enabled

Cross-Platform

  • [ ] SharedPreferences cache key matches between foreground write and background read
  • [ ] No direct RevenueCat/Firebase Auth SDK calls in background
  • [ ] Timeout handling with platform-appropriate limits
  • [ ] Tests for cache key consistency

Testing Strategy

Unit Tests

test('foreground/background cache key consistency', () async {
  SharedPreferences.setMockInitialValues({});
  final prefs = await SharedPreferences.getInstance();
  final repo = AgentRepository(prefs, keyPrefix: 'myapp');

  // Simulate foreground write
  await prefs.setBool('myapp_subscription_cached', true);

  // Background read should match
  expect(repo.getCachedSubscriptionStatus(), true);
});

Integration Tests

  1. Build release APK/IPA
  2. Install on device
  3. Enable the background task
  4. Kill the app
  5. Wait for task to trigger (or force-trigger)
  6. Check logs/SharedPreferences for evidence of execution

iOS Simulator Testing

# 1. Run app on simulator
# 2. Background the app
# 3. Force fetch:
xcrun simctl background_fetch booted com.example.myapp
# 4. Check console logs in Xcode

Related skills

  • flutter-verify — use to verify background tasks are executing correctly on real devices and checking for runtime errors.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.