Background services in Flutter

Background services in Flutter

Let’s talk more than just rows and columns

In mobile applications, background services allow certain tasks to run even when the app is not actively in use by the user. These services are essential for enhancing user experience by providing timely information, performing periodic tasks, and supporting features like location tracking, notifications, and syncing. Here are the main types of background services commonly used in mobile app development and how they are implemented in Flutter with examples :

Here's a detailed look at how to work with various types of background services in Flutter, including some code samples to illustrate their implementation.


1. Foreground Services

To run tasks that are visible to the user, like media playback, you can use plugins like audio_service. For advanced customization, you may need to write platform-specific code for Android or use background modes on iOS.

Code Example with audio_service for Media Playback

First, add audio_service and just_audio for audio playback in pubspec.yaml:

dependencies:
  audio_service: ^0.18.0
  just_audio: ^0.9.12

Then initialize the audio service:

import 'package:audio_service/audio_service.dart';
import 'package:just_audio/just_audio.dart';

AudioHandler audioHandler;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  audioHandler = await AudioService.init(
    builder: () => MyAudioHandler(),
    config: AudioServiceConfig(
      androidNotificationChannelId: 'com.example.myapp.audio',
      androidNotificationChannelName: 'Audio Playback',
    ),
  );
  runApp(MyApp());
}

class MyAudioHandler extends BaseAudioHandler {
  final _player = AudioPlayer();

  MyAudioHandler() {
    _player.setUrl("https://example.com/audio.mp3");
  }

  Future<void> play() => _player.play();
  Future<void> pause() => _player.pause();
}

This code sets up a basic foreground service for audio playback that can keep running even when the app is backgrounded.


2. Background Fetch

To fetch data periodically in the background, use background_fetch.

Code Example with background_fetch

Add the dependency in pubspec.yaml:

dependencies:
  background_fetch: ^0.7.3

In your main Dart file:

import 'package:background_fetch/background_fetch.dart';

void backgroundFetchHeadlessTask(HeadlessTask task) async {
  BackgroundFetch.finish(task.taskId);
}

void main() {
  runApp(MyApp());
  BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  void initState() {
    super.initState();
    BackgroundFetch.configure(BackgroundFetchConfig(
      minimumFetchInterval: 15,
      stopOnTerminate: false,
      enableHeadless: true,
    ), _onBackgroundFetch);
  }

  void _onBackgroundFetch(String taskId) async {
    // Fetch your data here and update the app.
    BackgroundFetch.finish(taskId);
  }
}

The _onBackgroundFetch method will be called every 15 minutes or so, allowing you to perform data fetches or other tasks.


3. Push Notifications

Firebase Cloud Messaging (FCM) is used to handle notifications, even when the app is closed or backgrounded.

Code Example with firebase_messaging

Add firebase_messaging to pubspec.yaml:

dependencies:
  firebase_messaging: ^11.2.0

Then configure FCM:

import 'package:firebase_messaging/firebase_messaging.dart';

class PushNotificationService {
  final FirebaseMessaging _fcm = FirebaseMessaging.instance;

  Future<void> initialize() async {
    // Request permission on iOS
    NotificationSettings settings = await _fcm.requestPermission();

    // Listen for messages while app is in the foreground
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      print('Received a foreground message: ${message.notification}');
    });

    // Handle background messages
    FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  }

  static Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
    print("Handling a background message: ${message.notification}");
  }
}

This code configures FCM to receive notifications both in the foreground and background.


4. Location Services

For background location tracking, use plugins like flutter_background_geolocation.

Code Example with flutter_background_geolocation

Add to pubspec.yaml:

dependencies:
  flutter_background_geolocation: ^1.8.0

Then configure background location tracking:

import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;

void main() {
  runApp(MyApp());

  bg.BackgroundGeolocation.onLocation((bg.Location location) {
    print('[location] ${location.coords}');
  });

  bg.BackgroundGeolocation.ready(bg.Config(
    desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH,
    distanceFilter: 10.0,
    stopOnTerminate: false,
    startOnBoot: true,
  )).then((bg.State state) {
    if (!state.enabled) {
      bg.BackgroundGeolocation.start();
    }
  });
}

This code sets up continuous location tracking in the background.


5. Periodic Tasks

You can use workmanager to schedule periodic background tasks.

Code Example with workmanager

Add the dependency:

dependencies:
  workmanager: ^0.4.1

Then initialize periodic tasks:

import 'package:workmanager/workmanager.dart';

void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) {
    print("Running background task: $task");
    return Future.value(true);
  });
}

void main() {
  runApp(MyApp());
  Workmanager().initialize(callbackDispatcher, isInDebugMode: true);
  Workmanager().registerPeriodicTask(
    "1",
    "simplePeriodicTask",
    frequency: Duration(minutes: 15),
  );
}

In this example, callbackDispatcher is called every 15 minutes to execute the task.


6. Alarm Services

Use android_alarm_manager to set alarms on Android.

Code Example with android_alarm_manager

Add android_alarm_manager to pubspec.yaml:

dependencies:
  android_alarm_manager: ^0.4.5+15

Then set an alarm:

import 'package:android_alarm_manager/android_alarm_manager.dart';

void main() async {
  runApp(MyApp());
  await AndroidAlarmManager.initialize();
  AndroidAlarmManager.oneShot(Duration(seconds: 5), 0, alarmCallback);
}

void alarmCallback() {
  print("Alarm triggered!");
}

This example schedules a one-shot alarm that triggers after 5 seconds.


7. Bluetooth and NFC Background Tasks

Use flutter_blue for Bluetooth and custom platform code for NFC.

Code Example with flutter_blue

Add to pubspec.yaml:

dependencies:
  flutter_blue: ^0.8.0

Then configure Bluetooth scanning:

import 'package:flutter_blue/flutter_blue.dart';

FlutterBlue flutterBlue = FlutterBlue.instance;

void startBluetoothScan() {
  flutterBlue.startScan(timeout: Duration(seconds: 4)).listen((scanResult) {
    print('Device found: ${scanResult.device.name}');
  });
}

void main() {
  runApp(MyApp());
  startBluetoothScan();
}

These code samples provide a foundation for handling background services in Flutter across different functionalities. However, it’s essential to carefully test these services across devices and platforms to handle the differences in background restrictions effectively.