Background Services
The flutter_background_services package allows you to manage background services in Flutter, providing a way to perform tasks even when the app is not in the foreground. Here’s a guide on how to use it for both Android and iOS:
Setup
Add Dependency:
Add flutter_background_services to your pubspec.yaml file:
dependencies: flutter: sdk: flutter flutter_background_services: ^1.0.0
Configure Android Manifest:
Add necessary permissions and service definitions in AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.example"> ... <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Permission to use here depends on the value you picked for foregroundServiceType - see the Android documentation. Eg, if you picked 'location', use 'android.permission.FOREGROUND_SERVICE_LOCATION' --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_..." /> <application android:label="example" android:name="${applicationName}" android:icon="@mipmap/ic_launcher" ...> <activity android:name=".MainActivity" android:exported="true" ...> <!--Add this--> <service android:name="id.flutter.flutter_background_service.BackgroundService" android:foregroundServiceType="WhatForegroundServiceTypeDoYouWant" /> <!--end--> ... ... </application> </manifest>
Create Service Function:
Implement the background service in your Dart code. Define what the service should do in the background.
import 'package:flutter_background_services/flutter_background_services.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); await initializeService(); runApp(MyApp()); } Future<void> initializeService() async { final service = FlutterBackgroundService(); /// OPTIONAL, using custom notification channel id const AndroidNotificationChannel channel = AndroidNotificationChannel( 'my_foreground', // id 'MY FOREGROUND SERVICE', // title description: 'This channel is used for important notifications.', // description importance: Importance.low, // importance must be at low or higher level ); final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); if (Platform.isIOS || Platform.isAndroid) { await flutterLocalNotificationsPlugin.initialize( const InitializationSettings( iOS: DarwinInitializationSettings(), android: AndroidInitializationSettings('ic_bg_service_small'), ), ); } await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(channel); await service.configure( androidConfiguration: AndroidConfiguration( // this will be executed when app is in foreground or background in separated isolate onStart: onStart, // auto start service autoStart: true, isForegroundMode: true, notificationChannelId: 'my_foreground', initialNotificationTitle: 'AWESOME SERVICE', initialNotificationContent: 'Initializing', foregroundServiceNotificationId: 888, foregroundServiceTypes: [AndroidForegroundType.location], ), iosConfiguration: IosConfiguration( // auto start service autoStart: true, // this will be executed when app is in foreground in separated isolate onForeground: onStart, // you have to enable background fetch capability on xcode project onBackground: onIosBackground, ), ); } // to ensure this is executed // run app from xcode, then from xcode menu, select Simulate Background Fetch @pragma('vm:entry-point') Future<bool> onIosBackground(ServiceInstance service) async { WidgetsFlutterBinding.ensureInitialized(); DartPluginRegistrant.ensureInitialized(); SharedPreferences preferences = await SharedPreferences.getInstance(); await preferences.reload(); final log = preferences.getStringList('log') ?? <String>[]; log.add(DateTime.now().toIso8601String()); await preferences.setStringList('log', log); return true; } @pragma('vm:entry-point') void onStart(ServiceInstance service) async { // Only available for flutter 3.0.0 and later DartPluginRegistrant.ensureInitialized(); // For flutter prior to version 3.0.0 // We have to register the plugin manually SharedPreferences preferences = await SharedPreferences.getInstance(); await preferences.setString("hello", "world"); /// OPTIONAL when use custom notification final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); if (service is AndroidServiceInstance) { service.on('setAsForeground').listen((event) { service.setAsForegroundService(); }); service.on('setAsBackground').listen((event) { service.setAsBackgroundService(); }); } service.on('stopService').listen((event) { service.stopSelf(); }); // bring to foreground Timer.periodic(const Duration(seconds: 1), (timer) async { if (service is AndroidServiceInstance) { if (await service.isForegroundService()) { /// OPTIONAL for use custom notification /// the notification id must be equals with AndroidConfiguration when you call configure() method. flutterLocalNotificationsPlugin.show( 888, 'COOL SERVICE', 'Awesome ${DateTime.now()}', const NotificationDetails( android: AndroidNotificationDetails( 'my_foreground', 'MY FOREGROUND SERVICE', icon: 'ic_bg_service_small', ongoing: true, ), ), ); // if you don't using custom notification, uncomment this service.setForegroundNotificationInfo( title: "My App Service", content: "Updated at ${DateTime.now()}", ); } } /// you can see this log in logcat debugPrint('FLUTTER BACKGROUND SERVICE: ${DateTime.now()}'); // test using external plugin final deviceInfo = DeviceInfoPlugin(); String? device; if (Platform.isAndroid) { final androidInfo = await deviceInfo.androidInfo; device = androidInfo.model; } else if (Platform.isIOS) { final iosInfo = await deviceInfo.iosInfo; device = iosInfo.model; } service.invoke( 'update', { "current_date": DateTime.now().toIso8601String(), "device": device, }, ); }); }
Configure Info.plist:
Update Info.plist to include the required permissions and capabilities:
<key>BGTaskSchedulerPermittedIdentifiers</key> <array> <string>dev.flutter.background.refresh</string> </array>
Note that iOS imposes strict limitations on background tasks. Ensure you adhere to Apple’s guidelines for background execution to avoid your app being rejected during review.