Android foreground services can be stopped and restarted by the system (process death, reboot, app update, auto-restart after crash). The library persists service state and configuration to SharedPreferences so the service can be reconstructed without Dart involvement.
The library uses named SharedPreferences files with the prefix
com.pravera.flutter_foreground_task.prefs. and never touches the
app's default preferences (PreferenceManager.getDefaultSharedPreferences).
Each logical group of data gets its own XML file on disk:
| Preference file name | Contents |
|---|---|
...prefs.NOTIFICATION_PERMISSION_STATUS |
Previous notification permission outcomes (for permanent-denial detection) |
...prefs.FOREGROUND_SERVICE_STATUS |
Which lifecycle action the service should perform next |
...prefs.FOREGROUND_SERVICE_TYPES |
Foreground service type bitflags (API 29+) |
...prefs.NOTIFICATION_OPTIONS |
Notification channel settings and visible content (title, text, icon, buttons, initial route) |
...prefs.FOREGROUND_TASK_OPTIONS |
Task event action, lifecycle flags (autoRunOnBoot, allowWakeLock, etc.), stopWithTask, and callback handle |
All key constants are defined in PreferencesKey.kt.
| Key | Type | Purpose |
|---|---|---|
foregroundServiceAction |
String |
Lifecycle action (API_START, API_STOP, API_RESTART, API_UPDATE, REBOOT, RESTART) |
| Key | Type | Purpose |
|---|---|---|
serviceTypes |
Int |
Bitwise OR of ServiceInfo.FOREGROUND_SERVICE_TYPE_* flags |
| Key | Type | Purpose |
|---|---|---|
serviceId |
Int |
Notification/service ID |
notificationId |
Int |
Legacy fallback for service ID |
notificationChannelId |
String |
Channel ID |
notificationChannelName |
String |
Channel display name |
notificationChannelDescription |
String? |
Channel description |
notificationChannelImportance |
Int |
Channel importance level |
notificationPriority |
Int |
Priority for pre-O devices |
enableVibration |
Boolean |
Vibration toggle |
playSound |
Boolean |
Sound toggle |
showWhen |
Boolean |
Timestamp visibility |
showBadge |
Boolean |
Badge visibility |
onlyAlertOnce |
Boolean |
Alert-once flag |
visibility |
Int |
Lock screen visibility |
notificationContentTitle |
String |
Notification title |
notificationContentText |
String |
Notification body |
icon |
String (JSON) |
Custom icon metadata |
buttons |
String (JSON array) |
Action buttons |
initialRoute |
String? |
Flutter route on notification tap |
| Key | Type | Purpose |
|---|---|---|
taskEventAction |
String (JSON) |
Event type and interval |
interval |
Long |
Deprecated: repeat interval |
isOnceEvent |
Boolean |
Deprecated: one-shot flag |
autoRunOnBoot |
Boolean |
Restart on device boot |
autoRunOnMyPackageReplaced |
Boolean |
Restart after app update |
allowWakeLock |
Boolean |
Acquire partial wake lock |
allowWifiLock |
Boolean |
Acquire Wi-Fi lock |
allowAutoRestart |
Boolean |
Auto-restart after crash |
stopWithTask |
Boolean? |
Stop when task is removed |
callbackHandle |
Long |
Dart isolate callback handle |
| Key | Type | Purpose |
|---|---|---|
| (permission string) | String |
Previous grant/deny result for permanent-denial detection |
When using multiple services, each service id gets its own set of preference files. The file name is constructed as:
- Default service (
"default"):prefix + name— e.g.com.pravera.flutter_foreground_task.prefs.NOTIFICATION_OPTIONS. This is identical to the legacy single-service layout, so existing installs keep their data with zero migration. - Custom service id:
prefix + serviceId + "." + name— e.g.com.pravera.flutter_foreground_task.prefs.locationTracker.NOTIFICATION_OPTIONS.
This isolation is handled by ForegroundTaskStorageProvider.getPreferences(context, serviceId, name). The legacy two-argument overload getPreferences(context, name) defaults to "default" for backward compatibility.
All persistence goes through model companion objects (getData, setData, updateData, clearData) backed by a ForegroundTaskStorageProvider. The provider returns SharedPreferences instances for each named file and can be reconfigured with a custom prefix from Dart. All model companions accept an optional serviceId parameter (defaulting to "default") to scope reads and writes.
Dart (AndroidNotificationOptions)
└─ method channel ──► ForegroundServiceManager.start(serviceId, ...)
└─ Model.setData(context, serviceId, args)
└─ ForegroundTaskStorageProvider.getPreferences(context, serviceId, name)
└─ context.getSharedPreferences(scopedName, MODE_PRIVATE)
Direct SharedPreferences reads also occur in:
FlutterForegroundServiceBase.onStartCommand-- readsstopWithTaskto install visibility tracking (inherited byForegroundServiceand any user subclass)ForegroundServiceUtils.isSetStopWithTaskFlag-- readsstopWithTaskfor reboot/restart receivers (now takes aserviceIdargument)
| Aspect | Android | iOS |
|---|---|---|
| Storage | Named SharedPreferences files |
UserDefaults with custom suite name |
| Namespace isolation | Fully prefixed file names | Separate suite (com.pravera.flutter_foreground_task) |
| Default preferences pollution | None | None (after refactor) |
| Abstraction | ForegroundTaskStorageProvider |
ForegroundTaskStorage protocol + ForegroundTaskStorageProvider |
| Dart configuration | storagePrefix on AndroidNotificationOptions |
storageSuiteName on IOSNotificationOptions |
Separately from the Android-side persistence described above, the library exposes a Dart storage API (FlutterForegroundTask.saveData, getData, etc.) backed by SharedPreferences. Those keys are prefixed with com.pravera.flutter_foreground_task.prefs. and are unrelated to the native-side persistence documented here.