Background Services in Flutter Add-to-App Case


Background operations are often used in Flutter applications. Yet, things might become more challenging when integrating Flutter with already-built native apps. We had to design business operations that needed additional work to be accomplished in the backdrop of a Flutter component as part of concrete evidence that we created for a customer in the banking industry. The native iOS and Android applications were to be updated with this module.

Visit BOSC TECH if you are ready to hire flutter expert from  See our case study and code samples to build background operations in a Flutter add-to-app.

Background Procedures – Description Of The Case

Mobile applications may need to run some tasks in the background. Making it operate consistently is a non-trivial technological challenge, and certain operating system-related limitations must be considered constantly.

Several intriguing use cases are covered by background processing in mobile apps, including:

  • Activity monitoring (fitness apps)
  • Location monitoring
  • Playback of media, including voice and video calls
  • Large file uploads and data downloads might take a long time.
  • Using Bluetooth to communicate with devices (e.g., beacons)
  • Business logic is carried out asynchronously through asynchronous business processes, which can also react asynchronously to changes.

Consider the following broad situation. The user uses the native app interface to navigate and launch a Flutter screen. There is a button to start background operations on the Flutter page. A background process is undertaken by pressing this button that will run even when the user exits the application.

After the background operation is complete, we want to display another Flutter screen with dialogue. This dialogue may show on any screen, native or Flutter, inside the app. We may sound a push notification that will launch the dialogue screen if the program is not running in the foreground. This is a more common situation.

Adding Background Services Is a Very Isolated Idea

To put that process into action, we require multitasking. The maritime routes of native and Flutter screens may overlap as we are working only with Flutter add-to-app situations. As a result, we will need several Flutter execution contexts as we cannot utilize the same Flutter processor for the two displays and the backend service.

Dart uses the idea of the isolate to support parallelism. Isolates do not share any modified objects with other isolates and only employ one operation thread. Isolates vary from conventional lines, known from different dialects, in that they use separate memory. Each isolate has an event loop that handles events. By using so-called ports, isolated entities can exchange communications with one another.

As a result, in our situation, we will require three isolates:

  • The primary isolate, which displays the initial Flutter screen;
  • Calculations are done in the background using background isolate,
  • And the dialogue is displayed using exchange isolate when they are complete.

Based on the idea of Flutter Engine, the execution environment operating within the native app, the Flutter add-to-app technique is a hybrid of both Flutter and React.js. We require numerous engines if we wish to have multiple isolates. The Flutter team recently introduced a brand-new idea called FlutterEngineGroup, which enables engine instances to pool memory resources and drastically reduce resource use. To increase our solution’s performance, we will use this feature. A different engine will power each isolate in the group.

Background solutions: native code and the Flutter add-on application

Create foundation classes for guests and clients, acting as our isolates’ generic communication system. Isolates will enroll with hosts, who will receive communications from other isolates. To communicate with the isolation, other isolates will employ clients. We would make those types general to enable isolates to design their messaging contracts.

abstract class IsolateClient<T> {


  final String _isolateName;


  SendPort? get sendPort => IsolateNameServer.lookupPortByName(_isolateName);


  void send(T message) => sendPort?.send(message);


const _isolateName = ‘mainIsolate’;

abstract class MainIsolateMessage {}

class BackgroundServiceStarted extends MainIsolateMessage {}

class MainIsolateClient extends IsolateClient<MainIsolateMessage> {

  MainIsolateClient() : super(_isolateName);


class MainIsolateHost extends IsolateHost<MainIsolateMessage> {

  MainIsolateHost._(ReceivePort port)

: super(receivePort: port, isolateName: _isolateName);

  factory MainIsolateHost.register() {

return MainIsolateHost._(registerIsolate(_isolateName));




Moreover, a method channel-based communication interface between Dart with native code must be established. We will utilize the pigeon package to spend time writing code duplication in Dart, Swift, and Kotlin. It is a code creator for creating typesafe contracts that allow Flutter and the hosting platform to communicate. With Pigeon, you may write a Dart schema file and have the package produce model and method stream invocation files in Dart, Objective-C, and Java.

This will be our Dart schema:


abstract class NativeMainApi {

  void startService(ComputationNotification notification);

  void stopService();




abstract class NativeDialogApi {

  void closeDialog();




abstract class NativeBackgroundServiceApi {

  void stopService();

  void openDialog();

  void updateNotification(ComputationNotification notification);



class ComputationNotification {

  late final String title;

  late final String message;

  late final int percentProgress;



A native interface defined by HostApis is usable from Dart programming. Pigeon also provides FlutterApis for the native platform to Flutter interaction, but for simplification, we will only utilize one-way exchange in this example. We create unique APIs for every isolation.

The calculation can be started and stopped by the primary isolate. An interface for terminating the native service, launching a Flutter dialogue, and modifying a notice shown on the system tray is defined by the background service isolate. The dialogue box might automatically shut down the engine.

Each isolate needs its entryway (the primary function). Either separate documents or a separate file can be used to specify them. But, in the latter scenario, we must remember to include the pragma tag to ensure that the native code may call the method and that its name will not be modified or removed during assembly improvements.

void main() {


  runApp(const MainApp());




void backgroundServiceMain() {






void dialogMain() {


  runApp(const DialogApp());



Native iOS app background resources

We chose background tasks on iOS since they met our needs and were easy enough to employ (the procedure had a time limit). You may use the Parallel Fetch API for even more complicated circumstances; the code would be similar.

This line of code launches Dart services and starts a backstory task to allow the program to run continuously in the background.

private let dartEntrypoint = “backgroundServiceMain”


func start(notification: LNCDComputationNotification) {

if engine == nil {

     let appDelegate = UIApplication.shared.delegate as! AppDelegate

     let engine = appDelegate.flutterEngineGroup.makeEngine(withEntrypoint: dartEntrypoint, libraryURI: nil)



backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(

  withName: backgroundTaskName,

     expirationHandler: {



lastNotification = notification


Background functions in native Android applications

The forefront service, which shows a system notification to ensure that the user is informed that the software is working even when they are not engaging with it, will serve as the foundation for our Android implementation. An equivalent implementation would make use of a background service.

We will employ three actions altogether:

  • FlutterMainActivity with only a screen displaying a button to begin the calculation,
  • Native MainActivity with a switch to lead us to a Flutter action,
  • and FlutterDialogActivity to display a Flutter dialogue when the computation is finished.

The service will start Dart code in the following way:

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

if (engine == null) {


     val notification = ComputationServiceNotification.createNotification(applicationContext, intent!!)

     startForeground(SERVICE_ID, notification)



return super.onStartCommand(intent, flags, startId)



private fun startDartService() {

engine = FlutterUtils.createOrGetEngine(this, AppFlutterEngine.computationService)

engine!!.let {

     Api.NativeBackgroundServiceApi.setup(it.dartExecutor, ComputationServiceApiHandler(this))

     api = Api.FlutterMainApi(it.dartExecutor)




First, we check to verify if the machine is already operating. The foreground service is then started, a notice is created, and a FlutterEngine is created to run Dart, software in the background.

Some boilerplate is needed for the entire setup. The whole example is available here. You can also learn here how to create copyable text widget in Flutter.


This post demonstrated how to integrate Flutter with native apps to provide background processing. The most significant thing we have shown is that you can retain the entire main business logic cross-platform in Dart, despite the implementation needing to be more straightforward and having several pitfalls. The majority of native code is generic and for infrastructure. It is a requirement since Flutter has to be integrated with native applications.


Businesses may use Flutter to write and test the business logic only once and ensure that it functions identically in iOS and Android app applications. You may contact our staff through the BOSC TECH website if you require assistance with the Flutter add-to-app.

Leave a Reply

Your email address will not be published. Required fields are marked *