flutter_foreground_task 8.2.0 flutter_foreground_task: ^8.2.0 copied to clipboard
This plugin is used to implement a foreground service on the Android platform.
This plugin is used to implement a foreground service on the Android platform.
Features #
- Can perform repetitive tasks with foreground service.
- Provides a widget that minimize the app without closing it when the user presses the soft back button.
- Provides useful utilities that can use while performing tasks.
- Provides option to automatically resume foreground service on boot.
Getting started #
To use this plugin, add flutter_foreground_task
as a dependency in your pubspec.yaml file. For example:
dependencies:
flutter_foreground_task: ^8.2.0
After adding the flutter_foreground_task
plugin to the flutter project, we need to specify the permissions and service to use for this plugin to work properly.
🐤 Android #
Open the AndroidManifest.xml
file and specify the service inside the <application>
tag as follows. If you want the foreground service to run only when the app is running, add android:stopWithTask
option.
As it is mentioned in the Android Guidelines, in Android 14, to start a FG service, you need to specify its type.
You can read all the details in the Android Developer Page : https://developer.android.com/about/versions/14/changes/fgs-types-required
If you want to target Android 14 phones, you need to add a few lines to your manifest. Change the type with your type (all types are listed in the link above).
<!-- required -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- foregroundServiceType: dataSync -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<!-- foregroundServiceType: remoteMessaging -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING" />
<!-- Add android:stopWithTask option only when necessary. -->
<service
android:name="com.pravera.flutter_foreground_task.service.ForegroundService"
android:foregroundServiceType="dataSync|remoteMessaging" <!-- Here, chose the type according to your app -->
android:exported="false" />
Check runtime requirements before starting the service. If this requirement is not met, the foreground service cannot be started.
Runtime requirements are listed in the link above.
🐤 iOS #
We can also launch flutter_foreground_task
on the iOS platform. However, it has the following limitations.
- Works only on iOS 12.0 or later.
- If you force close an app in recent apps, the task will be destroyed immediately.
- The task cannot be started automatically on boot like Android OS.
- The task will run in the background for approximately 30 seconds due to background processing limitations. but it works fine in the foreground.
Objective-C:
-
To use this plugin developed in Swift language in a project using Objective-C, you need to add a bridge header. If you don't have an
ios/Runner/Runner-Bridging-Header.h
file in your project, check this page. -
Open the
ios/Runner/AppDelegate.swift
file and add the commented code.
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
// here
#import <flutter_foreground_task/FlutterForegroundTaskPlugin.h>
// here
void registerPlugins(NSObject<FlutterPluginRegistry>* registry) {
[GeneratedPluginRegistrant registerWithRegistry:registry];
}
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// here, Without this code the task will not work.
[FlutterForegroundTaskPlugin setPluginRegistrantCallback:registerPlugins];
if (@available(iOS 10.0, *)) {
[UNUserNotificationCenter currentNotificationCenter].delegate = (id<UNUserNotificationCenterDelegate>) self;
}
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
Swift:
- Declare the import statement below in the
ios/Runner/Runner-Bridging-Header.h
file.
#import <flutter_foreground_task/FlutterForegroundTaskPlugin.h>
- Open the
ios/Runner/AppDelegate.swift
file and add the commented code.
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
// here, Without this code the task will not work.
SwiftFlutterForegroundTaskPlugin.setPluginRegistrantCallback(registerPlugins)
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
// here
func registerPlugins(registry: FlutterPluginRegistry) {
GeneratedPluginRegistrant.register(with: registry)
}
Configuring background execution modes
Background mode settings are required for tasks to be processed in the background.
See this page for settings.
How to use #
🐥 step by step #
- Initialize port for communication between TaskHandler and UI.
void main() {
// Initialize port for communication between TaskHandler and UI.
FlutterForegroundTask.initCommunicationPort();
runApp(const ExampleApp());
}
- Write a
TaskHandler
and acallback
to request starting a TaskHandler.
// The callback function should always be a top-level function.
@pragma('vm:entry-point')
void startCallback() {
// The setTaskHandler function must be called to handle the task in the background.
FlutterForegroundTask.setTaskHandler(MyTaskHandler());
}
class MyTaskHandler extends TaskHandler {
// Called when the task is started.
@override
void onStart(DateTime timestamp) {
print('onStart');
}
// Called every [ForegroundTaskOptions.interval] milliseconds.
@override
void onRepeatEvent(DateTime timestamp) {
// Send data to main isolate.
final Map<String, dynamic> data = {
"timestampMillis": timestamp.millisecondsSinceEpoch,
};
FlutterForegroundTask.sendDataToMain(data);
}
// Called when the task is destroyed.
@override
void onDestroy(DateTime timestamp) {
print('onDestroy');
}
// Called when data is sent using [FlutterForegroundTask.sendDataToTask].
@override
void onReceiveData(Object data) {
print('onReceiveData: $data');
}
// Called when the notification button is pressed.
@override
void onNotificationButtonPressed(String id) {
print('onNotificationButtonPressed: $id');
}
// Called when the notification itself is pressed.
//
// AOS: "android.permission.SYSTEM_ALERT_WINDOW" permission must be granted
// for this function to be called.
@override
void onNotificationPressed() {
FlutterForegroundTask.launchApp('/');
print('onNotificationPressed');
}
// Called when the notification itself is dismissed.
//
// AOS: only work Android 14+
// iOS: only work iOS 10+
@override
void onNotificationDismissed() {
print('onNotificationDismissed');
}
}
- Add a callback to receive data sent from the TaskHandler. If the screen or controller is disposed, be sure to call the
removeTaskDataCallback
function.
void _onReceiveTaskData(dynamic data) {
if (data is Map<String, dynamic>) {
final dynamic timestampMillis = data["timestampMillis"];
if (timestampMillis != null) {
final DateTime timestamp =
DateTime.fromMillisecondsSinceEpoch(timestampMillis, isUtc: true);
print('timestamp: ${timestamp.toString()}');
}
}
}
@override
void initState() {
super.initState();
// Add a callback to receive data sent from the TaskHandler.
FlutterForegroundTask.addTaskDataCallback(_onReceiveTaskData);
}
@override
void dispose() {
// Remove a callback to receive data sent from the TaskHandler.
FlutterForegroundTask.removeTaskDataCallback(_onReceiveTaskData);
super.dispose();
}
- Request permissions and initialize the service.
Future<void> _requestPermissions() async {
// Android 13+, you need to allow notification permission to display foreground service notification.
//
// iOS: If you need notification, ask for permission.
final NotificationPermission notificationPermissionStatus =
await FlutterForegroundTask.checkNotificationPermission();
if (notificationPermissionStatus != NotificationPermission.granted) {
await FlutterForegroundTask.requestNotificationPermission();
}
if (Platform.isAndroid) {
// "android.permission.SYSTEM_ALERT_WINDOW" permission must be granted for
// onNotificationPressed function to be called.
//
// When the notification is pressed while permission is denied,
// the onNotificationPressed function is not called and the app opens.
//
// If you do not use the onNotificationPressed or launchApp function,
// you do not need to write this code.
if (!await FlutterForegroundTask.canDrawOverlays) {
// This function requires `android.permission.SYSTEM_ALERT_WINDOW` permission.
await FlutterForegroundTask.openSystemAlertWindowSettings();
}
// Android 12+, there are restrictions on starting a foreground service.
//
// To restart the service on device reboot or unexpected problem, you need to allow below permission.
if (!await FlutterForegroundTask.isIgnoringBatteryOptimizations) {
// This function requires `android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` permission.
await FlutterForegroundTask.requestIgnoreBatteryOptimization();
}
}
}
Future<void> _initService() async {
FlutterForegroundTask.init(
androidNotificationOptions: AndroidNotificationOptions(
channelId: 'foreground_service',
channelName: 'Foreground Service Notification',
channelDescription:
'This notification appears when the foreground service is running.',
channelImportance: NotificationChannelImportance.LOW,
priority: NotificationPriority.LOW,
),
iosNotificationOptions: const IOSNotificationOptions(
showNotification: true,
playSound: false,
),
foregroundTaskOptions: const ForegroundTaskOptions(
interval: 5000,
isOnceEvent: false,
autoRunOnBoot: true,
autoRunOnMyPackageReplaced: true,
allowWakeLock: true,
allowWifiLock: true,
),
);
}
@override
void initState() {
super.initState();
// Add a callback to receive data sent from the TaskHandler.
FlutterForegroundTask.addTaskDataCallback(_onReceiveTaskData);
WidgetsBinding.instance.addPostFrameCallback((_) {
// Request permissions and initialize the service.
_requestPermissions();
_initService();
});
}
- Use
FlutterForegroundTask.startService
to start the service.startService
provides the following options:
notificationTitle
: The title to display in the notification.notificationText
: The text to display in the notification.notificationIcon
: The icon to display in the notification. (only work Android)notificationButtons
: The buttons to display in the notification. (can add 0~3 buttons)callback
: A top-level function that calls the setTaskHandler function.
Future<ServiceRequestResult> _startService() async {
if (await FlutterForegroundTask.isRunningService) {
return FlutterForegroundTask.restartService();
} else {
return FlutterForegroundTask.startService(
notificationTitle: 'Foreground Service is running',
notificationText: 'Tap to return to the app',
notificationIcon: null,
notificationButtons: [
const NotificationButton(id: 'btn_hello', text: 'hello'),
],
callback: startCallback,
);
}
}
Note
iOS Platform, notificationButtons
is not displayed directly in notification.
When the user slides down the notification, the button is displayed, so you need to guide the user on how to use it.
https://developer.apple.com/documentation/usernotifications/declaring-your-actionable-notification-types
If you know a better implementation, please let me know on GitHub :)
- Use
FlutterForegroundTask.updateService
to update the service. The options are the same as the start function.
@pragma('vm:entry-point')
void startCallback() {
FlutterForegroundTask.setTaskHandler(FirstTaskHandler());
}
class FirstTaskHandler extends TaskHandler {
int _count = 0;
@override
void onStart(DateTime timestamp) { }
@override
void onRepeatEvent(DateTime timestamp) {
if (_count == 10) {
FlutterForegroundTask.updateService(
foregroundTaskOptions: const ForegroundTaskOptions(interval: 1000),
callback: updateCallback,
);
} else {
FlutterForegroundTask.updateService(
notificationTitle: 'FirstTaskHandler',
notificationText: timestamp.toString(),
);
// Send data to main isolate.
final Map<String, dynamic> data = {
"timestampMillis": timestamp.millisecondsSinceEpoch,
};
FlutterForegroundTask.sendDataToMain(data);
}
_count++;
}
@override
void onDestroy(DateTime timestamp) { }
}
@pragma('vm:entry-point')
void updateCallback() {
FlutterForegroundTask.setTaskHandler(SecondTaskHandler());
}
class SecondTaskHandler extends TaskHandler {
@override
void onStart(DateTime timestamp) { }
@override
void onRepeatEvent(DateTime timestamp) {
FlutterForegroundTask.updateService(
notificationTitle: 'SecondTaskHandler',
notificationText: timestamp.toString(),
);
// Send data to main isolate.
final Map<String, dynamic> data = {
"timestampMillis": timestamp.millisecondsSinceEpoch,
};
FlutterForegroundTask.sendDataToMain(data);
}
@override
void onDestroy(DateTime timestamp) { }
}
- If you no longer use the service, call
FlutterForegroundTask.stopService
.
Future<ServiceRequestResult> _stopService() async {
return FlutterForegroundTask.stopService();
}
🐥 deepening #
This plugin supports two-way communication between TaskHandler and UI.
The send function can only send primitive type(int, double), String, Collection provided by Flutter.
If you want to send a custom object, send it in String format using jsonEncode and jsonDecode.
JSON and serialization >> https://docs.flutter.dev/data-and-backend/serialization/json
// TaskHandler
@override
void onStart(DateTime timestamp) {
// TaskHandler -> UI
FlutterForegroundTask.sendDataToMain(Object); // this
}
// Main(UI)::onReceiveTaskData
void _onReceiveTaskData(dynamic data) {
if (data is Map<String, dynamic>) {
final dynamic timestampMillis = data["timestampMillis"];
if (timestampMillis != null) {
final DateTime timestamp =
DateTime.fromMillisecondsSinceEpoch(timestampMillis, isUtc: true);
print('timestamp: ${timestamp.toString()}');
}
}
}
// Main(UI)
void _sendRandomData() {
final Random random = Random();
final int data = random.nextInt(100);
// UI -> TaskHandler
FlutterForegroundTask.sendDataToTask(data); // this
}
// TaskHandler::onReceiveData
@override
void onReceiveData(Object data) {
print('onReceiveData: $data');
}
And there are some functions for storing and managing data that are only used in this plugin.
void function() async {
await FlutterForegroundTask.getData(key: String);
await FlutterForegroundTask.getAllData();
await FlutterForegroundTask.saveData(key: String, value: Object);
await FlutterForegroundTask.removeData(key: String);
await FlutterForegroundTask.clearAllData();
}
If the plugin you want to use provides a stream, use it like this:
class MyTaskHandler extends TaskHandler {
StreamSubscription<Location>? _streamSubscription;
@override
void onStart(DateTime timestamp) {
_streamSubscription = FlLocation.getLocationStream().listen((location) {
FlutterForegroundTask.updateService(
notificationTitle: 'My Location',
notificationText: '${location.latitude}, ${location.longitude}',
);
// Send data to main isolate.
final String locationJson = jsonEncode(location.toJson());
FlutterForegroundTask.sendDataToMain(locationJson);
});
}
@override
void onRepeatEvent(DateTime timestamp) {
// not use
}
@override
void onDestroy(DateTime timestamp) {
_streamSubscription?.cancel();
_streamSubscription = null;
}
}
🐥 other example #
Migration (ver 8.0.0) #
- The
sendPort
parameter was removed from the service callback(onStart, onRepeatEvent, onDestroy)
// before
void onStart(DateTime timestamp, SendPort? sendPort) {
sendPort?.send(Object);
}
// after
void onStart(DateTime timestamp) {
// Send data to main isolate.
FlutterForegroundTask.sendDataToMain(Object);
}
FlutterForegroundTask.receivePort
getter function was removed.
// before
final ReceivePort? receivePort = FlutterForegroundTask.receivePort;
receivePort?.listen(_onReceiveTaskData)
receivePort?.close();
// atfer
void main() {
// Initialize port for communication between TaskHandler and UI.
FlutterForegroundTask.initCommunicationPort();
runApp(const ExampleApp());
}
FlutterForegroundTask.addTaskDataCallback(_onReceiveTaskData);
FlutterForegroundTask.removeTaskDataCallback(_onReceiveTaskData);
sendData
renamed tosendDataToTask
// before
FlutterForegroundTask.sendData(Object);
// after
FlutterForegroundTask.sendDataToTask(Object);
Models #
🐔 AndroidNotificationOptions #
Notification options for Android platform.
Property | Description |
---|---|
id |
Unique ID of the notification. |
channelId |
Unique ID of the notification channel. |
channelName |
The name of the notification channel. This value is displayed to the user in the notification settings. |
channelDescription |
The description of the notification channel. This value is displayed to the user in the notification settings. |
channelImportance |
The importance of the notification channel. The default is NotificationChannelImportance.DEFAULT . |
priority |
Priority of notifications for Android 7.1 and lower. The default is NotificationPriority.DEFAULT . |
enableVibration |
Whether to enable vibration when creating notifications. The default is false . |
playSound |
Whether to play sound when creating notifications. The default is false . |
showWhen |
Whether to show the timestamp when the notification was created in the content view. The default is false . |
visibility |
Control the level of detail displayed in notifications on the lock screen. The default is NotificationVisibility.VISIBILITY_PUBLIC . |
🐔 NotificationIconData #
Data for setting the notification icon.
Property | Description |
---|---|
resType |
The resource type of the notification icon. If the resource is in the drawable folder, set it to ResourceType.drawable , if the resource is in the mipmap folder, set it to ResourceType.mipmap . |
resPrefix |
The resource prefix of the notification icon. If the notification icon name is ic_simple_notification , set it to ResourcePrefix.ic and set name to simple_notification . |
name |
Notification icon name without prefix. |
🐔 ResourceType #
The resource type of the notification icon.
Value | Description |
---|---|
drawable |
A resources in the drawable folder. The drawable folder is where all kinds of images are stored. |
mipmap |
A resources in the mipmap folder. The mipmap folder is usually where the launcher icon image is stored. |
🐔 ResourcePrefix #
The resource prefix of the notification icon.
Value | Description |
---|---|
ic |
A resources with the ic_ prefix. |
img |
A resources with the img_ prefix. |
🐔 NotificationButton #
The button to display in the notification.
Property | Description |
---|---|
id |
The button identifier. |
text |
The text to display on the button. |
textColor |
The button text color. (only work Android) |
🐔 IOSNotificationOptions #
Notification options for iOS platform.
Property | Description |
---|---|
showNotification |
Whether to show notifications. The default is true . |
playSound |
Whether to play sound when creating notifications. The default is false . |
🐔 ForegroundTaskOptions #
Data class with foreground task options.
Property | Description |
---|---|
interval |
The task call interval in milliseconds. The default is 5000 . |
isOnceEvent |
Whether to invoke the onRepeatEvent of TaskHandler only once. The default is false . |
autoRunOnBoot |
Whether to automatically run foreground task on boot. The default is false . |
autoRunOnMyPackageReplaced |
Whether to automatically run foreground task when the app is updated to a new version. The default is false . |
allowWakeLock |
Whether to keep the CPU turned on. The default is true . |
allowWifiLock |
Allows an application to keep the Wi-Fi radio awake. The default is false . |
🐔 NotificationChannelImportance #
The importance of the notification channel. See https://developer.android.com/training/notify-user/channels?hl=ko#importance
Value | Description |
---|---|
NONE |
A notification with no importance: does not show in the shade. |
MIN |
Min notification importance: only shows in the shade, below the fold. |
LOW |
Low notification importance: shows in the shade, and potentially in the status bar (see shouldHideSilentStatusBarIcons()), but is not audibly intrusive. |
DEFAULT |
Default notification importance: shows everywhere, makes noise, but does not visually intrude. |
HIGH |
Higher notification importance: shows everywhere, makes noise and peeks. May use full screen intents. |
MAX |
Max notification importance: same as HIGH, but generally not used. |
🐔 NotificationPriority #
Priority of notifications for Android 7.1 and lower.
Value | Description |
---|---|
MIN |
No sound and does not appear in the status bar. |
LOW |
No sound. |
DEFAULT |
Makes a sound. |
HIGH |
Makes a sound and appears as a heads-up notification. |
MAX |
Same as HIGH, but used when you want to notify notification immediately. |
🐔 NotificationVisibility #
The level of detail displayed in notifications on the lock screen.
Value | Description |
---|---|
VISIBILITY_PUBLIC |
Show this notification in its entirety on all lockscreens. |
VISIBILITY_SECRET |
Do not reveal any part of this notification on a secure lockscreen. |
VISIBILITY_PRIVATE |
Show this notification on all lockscreens, but conceal sensitive or private information on secure lockscreens. |
🐔 ServiceRequestResult #
Result of service request.
Property | Description |
---|---|
success |
Whether the request was successful. |
error |
Error when the request failed. |
Utility methods #
🍭 minimizeApp #
Minimize the app to the background.
Warning It only works when the app is in the foreground.
void function() => FlutterForegroundTask.minimizeApp();
🍭 launchApp (Android) #
Launch the app at route
if it is not running otherwise open it.
void function() => FlutterForegroundTask.launchApp([route]);
It is also possible to pass a route to this function but the route will only be loaded if the app is not already running.
🍭 wakeUpScreen (Android) #
Wake up the screen of a device that is turned off.
void function() => FlutterForegroundTask.wakeUpScreen();
🍭 isIgnoringBatteryOptimizations (Android) #
Returns whether the app has been excluded from battery optimization.
Future<bool> function() => FlutterForegroundTask.isIgnoringBatteryOptimizations;
🍭 openIgnoreBatteryOptimizationSettings (Android) #
Open the settings page where you can set ignore battery optimization.
Warning It only works when the app is in the foreground.
Future<bool> function() => FlutterForegroundTask.openIgnoreBatteryOptimizationSettings();
🍭 requestIgnoreBatteryOptimization (Android) #
Request to ignore battery optimization.
This function requires android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
permission.
Warning It only works when the app is in the foreground.
Future<bool> function() => FlutterForegroundTask.requestIgnoreBatteryOptimization();
🍭 canDrawOverlays (Android) #
Returns whether the android.permission.SYSTEM_ALERT_WINDOW
permission is granted.
Future<bool> function() => FlutterForegroundTask.canDrawOverlays;
🍭 openSystemAlertWindowSettings (Android) #
Open the settings page where you can allow/deny the android.permission.SYSTEM_ALERT_WINDOW
permission.
Warning It only works when the app is in the foreground.
Future<bool> function() => FlutterForegroundTask.openSystemAlertWindowSettings();
🍭 isAppOnForeground #
Returns whether the app is in the foreground.
Future<bool> function() => FlutterForegroundTask.isAppOnForeground;
🍭 setOnLockScreenVisibility (Android) #
Toggles lockScreen visibility.
Warning It only works when the app is in the foreground.
void function() => FlutterForegroundTask.setOnLockScreenVisibility(true);
🍭 checkNotificationPermission #
Returns notification permission status.
Warning It only works when the app is in the foreground.
Future<NotificationPermission> function() => FlutterForegroundTask.checkNotificationPermission();
🍭 requestNotificationPermission #
Request notification permission.
Warning It only works when the app is in the foreground.
Future<NotificationPermission> function() => FlutterForegroundTask.requestNotificationPermission();
Support #
If you find any bugs or issues while using the plugin, please register an issues on GitHub. You can also contact us at hwj930513@naver.com.