{"id":22525318,"url":"https://github.com/plugfox/flutter_background_example","last_synced_at":"2025-08-03T21:32:27.614Z","repository":{"id":264520680,"uuid":"620387080","full_name":"PlugFox/flutter_background_example","owner":"PlugFox","description":"Flutter background example","archived":false,"fork":false,"pushed_at":"2023-04-04T18:40:32.000Z","size":232,"stargazers_count":10,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-24T20:43:26.421Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PlugFox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-28T15:23:32.000Z","updated_at":"2024-05-20T07:37:39.000Z","dependencies_parsed_at":"2024-11-24T20:43:33.038Z","dependency_job_id":"c1d75951-69ab-4a18-bd9e-ee73860ef401","html_url":"https://github.com/PlugFox/flutter_background_example","commit_stats":null,"previous_names":["plugfox/flutter_background_example"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlugFox%2Fflutter_background_example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlugFox%2Fflutter_background_example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlugFox%2Fflutter_background_example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PlugFox%2Fflutter_background_example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PlugFox","download_url":"https://codeload.github.com/PlugFox/flutter_background_example/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228567024,"owners_count":17937986,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-07T06:09:50.480Z","updated_at":"2024-12-07T06:09:51.282Z","avatar_url":"https://github.com/PlugFox.png","language":"Kotlin","readme":"# How to create background service\n\n### Important notes:\n\n- First of all, replace all \"`domain`\" and \"`tld`\" with your organization domain, and all \"`projectname`\" with your project name.\n- Points started with `[main]` are related to the main app and should be done in the package root directory.\n- Points started with `[background]` are related to background service and should be done in `./package/background` directory.\n- Implementation of the background service on android is also known as [Foreground service](https://developer.android.com/guide/components/foreground-services).\n- Our communication transport with native code through [Method Channels](https://docs.flutter.dev/development/platform-integration/platform-channels) is based on [pigeon](https://pub.dev/packages/pigeon) package.\n- Good idea to split the terminal into two panes, one for the `main` app and one for the `background` service.\n\n### Steps:\n\n1. `[main]` Create a new Flutter project (if you don't have one already)\n\n```bash\nflutter create -t app --project-name \"projectname\" --org \"tld.domain\" --description \"description\" --platforms android projectname\ncd projectname\n```\n\n---\n\n2. `[main]` Create a subdirectory and package for your service\n\n```bash\nmkdir -p ./package/background\nflutter create -t plugin --project-name \"background\" --org \"tld.domain\" --description \"Background service\" --platforms android ./package/background\nrm -rf ./package/background/example ./package/background/test ./package/background/lib\nmkdir -p ./package/background/lib/src\ntouch ./package/background/lib/background.dart\necho \"library background;\" \u003e ./package/background/lib/background.dart\n```\n\nAnd open the second pane in the terminal with `cd ./package/background/`\n\n---\n\n3. `[background]` Add \"pigeon\" dependency to your background `pubspec.yaml`\n\n```yaml\nflutter pub add meta\nflutter pub add --dev pigeon\n```\n\n---\n\n4. `[background]` Create directories for [pigeon](https://pub.dev/packages/pigeon) codegen.\n   The controller is our main class that will be used to communicate with the native code, and spawn and kill the background service.\n\n```bash\nmkdir -p \"./android/src/main/kotlin/tld/domain/background_controller/\"\nmkdir -p \"./lib/src/controller/\"\n```\n\n---\n\n5. `[background]` Add permissions and service for BackgroundService (also known as [Foreground service](https://developer.android.com/guide/components/foreground-services)) to `AndroidManifest.xml`.\n   Also, set permissions are required for the background service to start, work and restart after a reboot.\n\nAlso, if you need additional permissions, you can add them here. [Read more](https://developer.android.com/reference/android/Manifest.permission#FOREGROUND_SERVICE).\n\nFor example:\n\n- `FOREGROUND_SERVICE_CAMERA`\n- `FOREGROUND_SERVICE_CONNECTED_DEVICE`\n- `FOREGROUND_SERVICE_DATA_SYNC`\n- `FOREGROUND_SERVICE_HEALTH`\n- `FOREGROUND_SERVICE_LOCATION`\n- `FOREGROUND_SERVICE_MEDIA_PLAYBACK`\n- `FOREGROUND_SERVICE_MEDIA_PROJECTION`\n- `FOREGROUND_SERVICE_MICROPHONE`\n- `FOREGROUND_SERVICE_PHONE_CALL`\n- `FOREGROUND_SERVICE_REMOTE_MESSAGING`\n- `FOREGROUND_SERVICE_SPECIAL_USE`\n- `FOREGROUND_SERVICE_SYSTEM_EXEMPTED`\n- `ACCESS_FINE_LOCATION`, `ACCESS_COARSE_LOCATION`, `ACCESS_BACKGROUND_LOCATION`\n\n```bash\ncode ./android/src/main/AndroidManifest.xml\n```\n\n\u003c\u003c\n\n```xml\n\u003cmanifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  package=\"tld.domain.background\"\u003e\n\n    \u003c!-- Allows a regular application to use Service.startForeground. --\u003e\n    \u003cuses-permission android:name=\"android.permission.FOREGROUND_SERVICE\"/\u003e\n\n    \u003c!-- Allows an application to receive the Intent.ACTION_BOOT_COMPLETED\n            that is broadcast after the system finishes booting. --\u003e\n    \u003cuses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\" /\u003e\n\n    \u003c!-- Intent android.intent.action.BOOT_COMPLETED is received after a \"cold\" boot. --\u003e\n    \u003cuses-permission android:name=\"android.permission.BOOT_COMPLETED\" /\u003e\n\n    \u003c!-- Intent android.intent.action.QUICKBOOT_POWERON is received after a \"restart\" or a \"reboot\". --\u003e\n    \u003cuses-permission android:name=\"android.permission.QUICKBOOT_POWERON\" /\u003e\n\n    \u003c!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming. --\u003e\n    \u003cuses-permission android:name=\"android.permission.WAKE_LOCK\"/\u003e\n\n    \u003c!-- Allows applications to access information about networks. --\u003e\n    \u003cuses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" /\u003e\n\n    \u003c!-- Allows applications to open network sockets. --\u003e\n    \u003cuses-permission android:name=\"android.permission.INTERNET\" /\u003e\n\n\n    \u003capplication\u003e\n        \u003c!--\n          Foreground service\n          - - - - - - - - - - - - - - - - - -\n          Specify that the service is a foreground service that satisfies\n          a particular use case with \"foregroundServiceType\". For example,\n          a foreground service type of \"location\" indicates that an app\n          is getting the device's current location, usually to continue\n          a user-initiated action related to device location.\n\n          The \"dataSync\" indicates that the service is performing a sync operation,\n          such as downloading or uploading data from a remote server.\n\n          You can assign multiple foreground service types to a particular service.\n          - - - - - - - - - - - - - - - - - -\n          https://developer.android.com/guide/topics/manifest/service-element\n          --\u003e\n        \u003cservice android:name=\"tld.domain.background_service.BackgroundService\"\n                 android:enabled=\"true\"\n                 android:exported=\"true\"\n                 android:stopWithTask=\"false\"\n                 android:foregroundServiceType=\"dataSync\"\n                 /\u003e\n\n        \u003c!-- Restart service after reboot\n          https://developer.android.com/guide/topics/manifest/receiver-element\n          --\u003e\n        \u003creceiver\n                android:name=\"tld.domain.background_boot_receiver.BackgroundBootReceiver\"\n                android:enabled=\"true\"\n                android:exported=\"true\"\u003e\n            \u003cintent-filter\u003e\n                \u003caction android:name=\"android.intent.action.MY_PACKAGE_REPLACED\"/\u003e\n                \u003caction android:name=\"android.intent.action.BOOT_COMPLETED\"/\u003e\n                \u003caction android:name=\"android.intent.action.QUICKBOOT_POWERON\"/\u003e\n                \u003caction android:name=\"com.htc.intent.action.QUICKBOOT_POWERON\"/\u003e\n            \u003c/intent-filter\u003e\n        \u003c/receiver\u003e\n    \u003c/application\u003e\n\u003c/manifest\u003e\n```\n\n---\n\n6. `[background]` Create a pigeon's message class for communication with the native code.\n\n```bash\nmkdir -p ./pigeons\ncode ./pigeons/api.dart\n```\n\n\u003c\u003c\n\n```dart\nimport 'package:pigeon/pigeon.dart';\n\n/// Contains a boolean value.\nclass BooleanValue {\n  bool? value;\n}\n\n/// Payload for the message that will be sent to the native code\n/// and spawn the background service.\nclass OpenMessage {\n  int? entryPointRawHandler;\n}\n\n/// Message class for communication with the native code\n/// and the background service.\n///\n/// This class is used by the Dart code to send messages to the native code.\n@HostApi()\nabstract class ApiFromDart {\n  /// Open the background service.\n  @async\n  void open(OpenMessage openMessage);\n\n  BooleanValue isOpen();\n\n  /// Close the background service.\n  @async\n  void close();\n}\n\n/// Message class for communication with the native code\n/// and the background service.\n///\n/// This class is used by the native code to send messages to the Dart code.\n@FlutterApi()\nabstract class ApiToDart {\n  /// Called when the background service is opened.\n  void afterOpening();\n\n  /// Called when the background service is closed.\n  void afterClosing();\n}\n```\n\nCreate the following directories for pigeon code generation (if they do not exist)\n\n```bash\nmkdir -p ./lib/src/controller\nmkdir -p ./android/src/main/kotlin/com/vexus/background_controller/api\n```\n\nAnd run code generation\n\n```bash\nflutter pub run pigeon \\\n    --input \"pigeons/api.dart\" \\\n    --dart_out \"lib/src/controller/api.g.dart\" \\\n    --kotlin_out \"android/src/main/kotlin/com/vexus/background_controller/api/Api.kt\" \\\n    --kotlin_package \"com.vexus.background_controller.api\"\n```\n\n---\n\n7. `[background]` Add icon image for notification.\n   [Notification Icon Generator](https://romannurik.github.io/AndroidAssetStudio/icons-notification.html#source.type=clipart\u0026source.clipart=ac_unit\u0026source.space.trim=1\u0026source.space.pad=0\u0026name=ic_stat_ac_unit) will be helpful.\n   path: `android/src/main/res/drawable-*`\n\n---\n\n8. `[main]` Check the following in your \"`build.gradle`\" file:\n\n```bash\ncode ./android/build.gradle\n```\n\nAnd add native android dependencies.\n\n---\n\n9. `[background]` Create the entry point of the background service.\n\n```bash\ncode ./lib/src/main.dart\n```\n\n\u003c\u003c\n\n```dart\nimport 'dart:async';\nimport 'dart:developer' as developer;\n\n/// Background entry point\n@pragma('vm:entry-point')\nvoid main() =\u003e runZonedGuarded\u003cvoid\u003e(\n      () {\n        /* Your code */\n      },\n      (error, stackTrace) =\u003e developer.log(\n        'A global error has occurred: $error',\n        error: error,\n        stackTrace: stackTrace,\n        name: 'background',\n        level: 900,\n      ),\n    );\n```\n\n---\n\n10. `[background]` Create the controller and export it.\n\n```bash\ntouch ./lib/src/controller/controller.dart\necho 'export \"src/controller/controller.dart\";' \u003e\u003e ./lib/background.dart\ncode ./lib/src/controller/controller.dart\n```\n\n\u003c\u003c\n\n```dart\nimport 'dart:async';\nimport 'dart:developer' as dev;\nimport 'dart:ui' as ui;\n\nimport 'package:flutter/foundation.dart' show ChangeNotifier;\nimport 'package:meta/meta.dart';\n\nimport '../main.dart';\nimport 'api.g.dart' as g;\n\n/// The status of the background service.\nenum BackgroundStatus {\n  /// The background service is opening but not yet running.\n  opening('opening'),\n\n  /// The background service is running.\n  opened('opened'),\n\n  /// The background service is closing but not yet closed.\n  closing('closing'),\n\n  /// The background service is not running.\n  closed('closed');\n\n  const BackgroundStatus(this.name);\n\n  /// The name of the status.\n  final String name;\n\n  /// Is the background service opening?\n  bool get isOpened =\u003e this == opened;\n\n  /// Is the background service not opening?\n  bool get isClosed =\u003e this == closed;\n\n  /// In progress\n  bool get inProgress =\u003e !isOpened \u0026\u0026 !isClosed;\n\n  @override\n  String toString() =\u003e name;\n}\n\n/// The controller of the background service.\nclass Controller with ChangeNotifier implements g.ApiToDart {\n  /// The controller of the background service.\n  factory Controller() =\u003e _internalSingleton ??= Controller._internal();\n\n  Controller._internal() : _sender = g.ApiFromDart() {\n    g.ApiToDart.setup(this);\n    isOpen().ignore();\n    _watchdog = Timer.periodic(\n      const Duration(seconds: 60),\n      (_) =\u003e isOpen().ignore(),\n    );\n  }\n\n  static Controller? _internalSingleton;\n\n  /// Check if the background service\n  late final Timer _watchdog;\n\n  /// The sender of messages to the native code.\n  final g.ApiFromDart _sender;\n\n  BackgroundStatus _$status = BackgroundStatus.closed;\n  set _status(BackgroundStatus value) {\n    if (_$status == value) return;\n    _$status = value;\n    dev.log('Background status: ${value.name}', name: 'controller', level: 0);\n    notifyListeners();\n  }\n\n  /// The current status of the background service.\n  @nonVirtual\n  BackgroundStatus get status =\u003e _$status;\n\n  /// Open the background service.\n  @mustCallSuper\n  Future\u003cvoid\u003e open() async {\n    try {\n      _status = BackgroundStatus.opening;\n      final entryPointRawHandler = ui.PluginUtilities.getCallbackHandle(\n        main,\n      )?.toRawHandle();\n      if (entryPointRawHandler == null) {\n        throw UnsupportedError('Can not get the entry point callback handle.');\n      }\n      await _sender.open(\n        g.OpenMessage(entryPointRawHandler: entryPointRawHandler),\n      );\n      _status = BackgroundStatus.opened;\n    } on Object {\n      _status = BackgroundStatus.closed;\n      rethrow;\n    }\n  }\n\n  /// Close the background service.\n  @mustCallSuper\n  Future\u003cvoid\u003e close() async {\n    try {\n      _status = BackgroundStatus.closing;\n      await _sender.close();\n      _status = BackgroundStatus.closed;\n    } on Object {\n      _status = BackgroundStatus.closed;\n      rethrow;\n    }\n  }\n\n  /// Check if the background service is running.\n  /// Also it will update the [status] property.\n  /// And works as a health check.\n  @mustCallSuper\n  Future\u003cbool\u003e isOpen() async {\n    try {\n      final result = await _sender.isOpen().then\u003cbool?\u003e((v) =\u003e v.value);\n      switch (result) {\n        case true:\n          _status = BackgroundStatus.opened;\n          return false;\n        case false:\n          _status = BackgroundStatus.closed;\n          return false;\n        default:\n          throw StateError('Service status is unknown.');\n      }\n    } on Object {\n      _status = BackgroundStatus.closed;\n      rethrow;\n    }\n  }\n\n  /// Called when the background service is opened.\n  @override\n  @protected\n  @mustCallSuper\n  void afterOpening() {\n    dev.log('Background service is opened', name: 'controller', level: 0);\n    _status = BackgroundStatus.opened;\n  }\n\n  /// Called when the background service is closed.\n  @override\n  @protected\n  @mustCallSuper\n  void afterClosing() {\n    dev.log('Background service is closed', name: 'controller', level: 0);\n    _status = BackgroundStatus.closed;\n  }\n\n  /// Dispose the controller and subscriptions.\n  /// This method should not be called directly.\n  @override\n  @mustCallSuper\n  @visibleForTesting\n  void dispose() {\n    _internalSingleton = null;\n    _watchdog.cancel();\n    g.ApiToDart.setup(null);\n    super.dispose();\n  }\n}\n```\n\n---\n\n11. `[background]` Create the scope and export it.\n\n```bash\ntouch ./lib/src/controller/background_scope.dart\necho 'export \"src/controller/background_scope.dart\";' \u003e\u003e ./lib/background.dart\ncode ./lib/src/controller/background_scope.dart\n```\n\n\u003c\u003c\n\n```dart\nimport 'package:flutter/widgets.dart';\n\nimport 'controller.dart';\n\n/// {@template background_scope}\n/// BackgroundScope widget.\n/// {@endtemplate}\nclass BackgroundScope extends StatefulWidget {\n  /// {@macro background_scope}\n  const BackgroundScope({\n    required this.child,\n    this.autoOpen = false,\n    super.key,\n  });\n\n  /// Auto open the background service when the widget is mounted.\n  final bool autoOpen;\n\n  /// The widget below this widget in the tree.\n  final Widget child;\n\n  /// The state from the closest instance of this class\n  static BackgroundStatus statusOf(BuildContext context,\n          {bool listen = true}) =\u003e\n      _InhBackgroundScope.of(context, listen: listen)._status;\n\n  /// Open the background service.\n  static Future\u003cvoid\u003e openOf(BuildContext context) =\u003e\n      _InhBackgroundScope.of(context, listen: false)._controller.open();\n\n  /// Close the background service.\n  static Future\u003cvoid\u003e closeOf(BuildContext context) =\u003e\n      _InhBackgroundScope.of(context, listen: false)._controller.close();\n\n  /// Check if the background service is running.\n  static Future\u003cbool\u003e isOpenOf(BuildContext context) =\u003e\n      _InhBackgroundScope.of(context, listen: false)._controller.isOpen();\n\n  @override\n  State\u003cBackgroundScope\u003e createState() =\u003e _BackgroundScopeState();\n}\n\n/// State for widget BackgroundScope.\nclass _BackgroundScopeState extends State\u003cBackgroundScope\u003e {\n  final Controller _controller = Controller();\n  late BackgroundStatus _status = _controller.status;\n\n  @override\n  void initState() {\n    super.initState();\n    if (widget.autoOpen) _controller.open().ignore();\n    _controller.addListener(_onStatusChanged);\n  }\n\n  @override\n  void dispose() {\n    _controller.removeListener(_onStatusChanged);\n    super.dispose();\n  }\n\n  void _onStatusChanged() {\n    if (!mounted) return;\n    setState(() =\u003e _status = _controller.status);\n  }\n\n  @override\n  Widget build(BuildContext context) =\u003e _InhBackgroundScope(\n        controller: _controller,\n        status: _status,\n        child: widget.child,\n      );\n}\n\n/// Inherited widget for quick access in the element tree.\nclass _InhBackgroundScope extends InheritedWidget {\n  const _InhBackgroundScope({\n    required Controller controller,\n    required BackgroundStatus status,\n    required super.child,\n  })  : _controller = controller,\n        _status = status;\n\n  final Controller _controller;\n  final BackgroundStatus _status;\n\n  /// The state from the closest instance of this class\n  /// that encloses the given context, if any.\n  /// e.g. `_InheritedBackgroundScope.maybeOf(context)`.\n  static _InhBackgroundScope? maybeOf(BuildContext context,\n          {bool listen = false}) =\u003e\n      listen\n          ? context.dependOnInheritedWidgetOfExactType\u003c_InhBackgroundScope\u003e()\n          : (context\n              .getElementForInheritedWidgetOfExactType\u003c_InhBackgroundScope\u003e()\n              ?.widget as _InhBackgroundScope?);\n\n  static Never _notFoundInheritedWidgetOfExactType() =\u003e throw ArgumentError(\n        'Out of scope, not found inherited widget '\n            'a _InheritedBackgroundScope of the exact type',\n        'out_of_scope',\n      );\n\n  /// The state from the closest instance of this class\n  /// that encloses the given context.\n  /// e.g. `_InheritedBackgroundScope.of(context)`\n  static _InhBackgroundScope of(BuildContext context, {bool listen = false}) =\u003e\n      maybeOf(context, listen: listen) ?? _notFoundInheritedWidgetOfExactType();\n\n  @override\n  bool updateShouldNotify(covariant _InhBackgroundScope oldWidget) =\u003e\n      _status != oldWidget._status;\n}\n```\n\n---\n\n12. `[main]` Add the background package to the main `pubspec.yaml` file.\n\n```bash\ncode ./pubspec.yaml\n```\n\n\u003c\u003c\n\n```yaml\ndependencies:\n  # ...\n  background:\n    path: package/background\n```\n\n---\n\n13. `[background]` Create background service.\n\n```bash\nmkdir -p ./android/src/main/kotlin/tld/domain/background_service\ncode     ./android/src/main/kotlin/tld/domain/background_service/BackgroundService.kt\n```\n\n\u003c\u003c\n\n```kotlin\npackage tld.domain.background_service\n\nimport android.app.*\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\nimport android.os.Build\nimport android.os.IBinder\nimport android.util.Log\nimport android.widget.Toast\nimport androidx.core.app.NotificationCompat\nimport androidx.core.content.ContextCompat\nimport androidx.annotation.Keep\nimport io.flutter.FlutterInjector\nimport io.flutter.embedding.engine.FlutterEngine\nimport io.flutter.embedding.engine.dart.DartExecutor\nimport io.flutter.embedding.engine.loader.FlutterLoader\nimport io.flutter.view.FlutterCallbackInformation\nimport tld.domain.background.BackgroundPlugin\nimport tld.domain.background.R\n\n/// Android background service (Foreground Service)\n/// with FlutterEngine inside.\n///\n/// This service is started by [BackgroundService.startBackgroundService] and stopped by\n/// [BackgroundService.stopBackgroundService].\n///\n/// -------------------------\n/// Lifecycle\n/// -------------------------\n/// Initialization:\n/// 1. startBackgroundService\n/// 2. onCreate\n/// 3. createNotificationChannel\n/// 4. onStartCommand\n/// 5. initService\n/// 6. startForeground\n/// 7. startDartIsolate\n///\n/// Termination:\n/// 1. stopBackgroundService\n/// 2. onStartCommand\n/// 3. onDestroy\n/// 4. closeService\n/// 5. deleteNotificationChannel\nclass BackgroundService : Service() {\n    companion object {\n        /// Health check of FlutterEngine and ForegroundService\n        fun healthCheck(context: Context) : Boolean {\n            val dart = isExecutingDart\n            val service = isServiceRunning\n            fun log(message: String) {\n                Log.d(TAG, \"[healthCheck] $message\")\n                showToast(context, message)\n            }\n            return if (isExecutingDart \u0026\u0026 service) {\n                log(\"Background service is running\")\n                true\n            } else if (!isExecutingDart \u0026\u0026 !service) {\n                log(\"Background service is not running\")\n                false\n            } else if (!dart) {\n                log(\"Background service is running w/0 Dart\")\n                stopBackgroundService(context)\n                false\n            } else {\n                log(\"Dart is running w/0 Background service\")\n                stopBackgroundService(context)\n                false\n            }\n        }\n\n        /// Show Toast message\n        private fun showToast(context: Context, message: String) =\n            Toast.makeText(context, message, Toast.LENGTH_LONG).show()\n\n        /// Check if service is running\n        @Suppress(\"MemberVisibilityCanBePrivate\", \"unused\")\n        var isExecutingDart: Boolean\n            get() = flutterEngine?.dartExecutor?.isExecutingDart == true\n            private set(_) {}\n\n        /// Start Background Service by last callback information from SharedPreferences\n        @Suppress(\"MemberVisibilityCanBePrivate\", \"unused\")\n        fun startBackgroundService(context: Context) : Boolean {\n            with(BackgroundSharedPreferencesHelper.getLastCallbackInformation(context)) {\n                // e.g. Pair(\"package:background/src/main.dart\", \"main\")\n                if (this == null) {\n                    Log.d(TAG, \"[startBackgroundService] No last callback information found\")\n                    return false\n                }\n                return startBackgroundService(context, first, second)\n            }\n        }\n\n        /// Start Background Service by entryPointRawHandler\n        @Suppress(\"MemberVisibilityCanBePrivate\", \"unused\")\n        fun startBackgroundService(context: Context, entryPointRawHandler: Long) : Boolean {\n            Log.d(TAG, \"[startBackgroundService] Will try to start BackgroundService \" +\n                    \"by entryPointRawHandler: $entryPointRawHandler\")\n            return FlutterCallbackInformation.lookupCallbackInformation(entryPointRawHandler).let {\n                startBackgroundService(context, it.callbackLibraryPath, it.callbackName)\n            }\n        }\n\n        /// Start Background Service by callbackLibraryPath and callbackName\n        @Suppress(\"MemberVisibilityCanBePrivate\", \"unused\")\n        fun startBackgroundService(context: Context, callbackLibraryPath: String, callbackName: String) : Boolean {\n            if (callbackLibraryPath.isEmpty() || callbackName.isEmpty()) return false\n            if (isExecutingDart) stopBackgroundService(context)\n            Log.d(TAG, \"[startBackgroundService] Will try to start BackgroundService \" +\n                    \"by callbackLibraryPath: $callbackLibraryPath, callbackName: $callbackName\")\n            Intent(context, BackgroundService::class.java).apply {\n                putExtra(\"ContentTitle\", \"Background service enabled\")\n                putExtra(\"ContentText\", \"Background service has been enabled.\")\n                putExtra(CALLBACK_LIBRARY_PATH_KEY, callbackLibraryPath)\n                putExtra(CALLBACK_NAME_KEY, callbackName)\n                action = ACTION_START_FOREGROUND_SERVICE\n            }.also {\n                ContextCompat.startForegroundService(context, it)\n            }\n            BackgroundSharedPreferencesHelper.putLastCallbackInformation(context, callbackLibraryPath, callbackName)\n            return true\n        }\n\n        /// Stop the Background Service\n        @Suppress(\"MemberVisibilityCanBePrivate\", \"unused\")\n        fun stopBackgroundService(context: Context) {\n            if (!isExecutingDart) return\n            Log.d(TAG, \"[stopBackgroundService] Will try to stop BackgroundService\")\n            Intent(context, BackgroundService::class.java).apply {\n                action = ACTION_STOP_FOREGROUND_SERVICE\n                context.startService(this)\n            }\n            // Remove last callback information if foreground service\n            // should not be restarted after reboot:\n            BackgroundSharedPreferencesHelper.removeLastCallbackInformation(context)\n        }\n\n        private var flutterEngine: FlutterEngine? = null\n        private var isServiceRunning: Boolean = false\n        private const val TAG: String = \"BackgroundService\"\n        private const val FOREGROUND_SERVICE_ID: Int = 1\n        private const val NOTIFICATION_CHANNEL_ID: String = \"tld.domain.background_service.BackgroundServiceChannel\"\n        private const val NOTIFICATION_CHANNEL_NAME: String = \"Foreground Service Channel\"\n        private const val ACTION_START_FOREGROUND_SERVICE: String = \"ACTION_START_FOREGROUND_SERVICE\"\n        private const val ACTION_STOP_FOREGROUND_SERVICE: String = \"ACTION_STOP_FOREGROUND_SERVICE\"\n        private const val CALLBACK_LIBRARY_PATH_KEY: String = \"CALLBACK_LIBRARY_PATH\"\n        private const val CALLBACK_NAME_KEY: String = \"CALLBACK_NAME\"\n    }\n\n    /// On create service\n    override fun onCreate() {\n        isServiceRunning = true\n        super.onCreate()\n        Log.d(TAG, \"[onCreate] BackgroundService created\")\n        try {\n            createNotificationChannel()\n        } catch (exception: Throwable) {\n            Log.e(TAG, \"[onCreate] Error: ${exception.message}\", exception)\n        }\n    }\n\n    /// Handling incoming intents and start foreground service\n    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {\n        val action: String? = intent?.action\n        Log.d(TAG, String.format(\"[onStartCommand] %s\", action))\n        if (action != null) when (action) {\n            // For start foreground service\n            ACTION_START_FOREGROUND_SERVICE -\u003e {\n                initService(intent)\n                Log.d(TAG, String.format(\"[onStartCommand] Background service is started.\"))\n                Toast.makeText(applicationContext, \"Background service is started.\", Toast.LENGTH_LONG).show()\n                return START_STICKY // START_NOT_STICKY\n            }\n            // For stop foreground service\n            ACTION_STOP_FOREGROUND_SERVICE -\u003e {\n                Log.d(TAG, \"[onStartCommand] Stop foreground service and remove the notification.\")\n\n                // Stop foreground service and remove the notification.\n                stopForeground(true)\n\n                // Stop the foreground service.\n                stopSelf()\n\n                Log.d(TAG, String.format(\"[onStartCommand] Background service is stopped.\"))\n                Toast.makeText(applicationContext, \"Background service is stopped.\", Toast.LENGTH_LONG).show()\n            }\n            else -\u003e {\n                Log.d(TAG, String.format(\"[onStartCommand] Unknown action: %s\", action))\n            }\n        }\n        return START_STICKY\n    }\n\n    /// Called when the service is no longer used and is being destroyed permanently.\n    override fun onDestroy() {\n        isServiceRunning = false\n        Log.d(TAG, String.format(\"[onDestroy] Service is destroyed permanently.\"))\n        try {\n            closeService()\n        } catch (exception: Throwable) {\n            Log.d(TAG, \"[onDestroy] Kill current process to avoid memory leak in other plugin.\", exception)\n            android.os.Process.killProcess(android.os.Process.myPid())\n        }\n        super.onDestroy()\n    }\n\n    /// Called by the system every time a client explicitly starts\n    // the service by calling Context.startService(Intent),\n    override fun onBind(intent: Intent?): IBinder? = null\n\n    /// Initialize background service:\n    /// + Notification channel [NOTIFICATION_CHANNEL_ID]\n    /// + Message about started ForegroundService\n    /// + Start ForegroundService\n    /// + Create and start FlutterEngine\n    private fun initService(intent: Intent) {\n        Log.d(TAG, String.format(\"[initService] Initialize background service\"))\n        if (isExecutingDart) {\n            val exception = Exception(\"BackgroundService already running!\")\n            Log.e(TAG, \"[initService] BackgroundService already running!\", exception)\n            throw exception\n        }\n        try {\n            intent.let {\n                val callbackLibraryPath: String =\n                    it.getStringExtra(CALLBACK_LIBRARY_PATH_KEY) ?:\n                        throw Exception(\"CallbackLibraryPath not passed for dart entry point\")\n                val callbackName: String =\n                    it.getStringExtra(CALLBACK_NAME_KEY) ?:\n                        throw Exception(\"CallbackName not passed for dart entry point\")\n\n                //val packageName = applicationContext.packageName\n                //val i = packageManager.getLaunchIntentForPackage(packageName)\n                //var flags = PendingIntent.FLAG_CANCEL_CURRENT\n                //if (Build.VERSION.SDK_INT \u003e= Build.VERSION_CODES.S) {\n                //    flags = flags or PendingIntent.FLAG_MUTABLE\n                //}\n                //val pi = PendingIntent.getActivity(this@BackgroundService, 11, i, flags)\n\n                fun getNotification(): Notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)\n                    .setContentTitle(it.getStringExtra(\"ContentTitle\") ?: \"Background service enabled\")\n                    .setContentText(it.getStringExtra(\"ContentText\") ?: \"Background service has been enabled\")\n                    .setSmallIcon(R.drawable.ic_background) // android.R.drawable.ic_dialog_info\n                    .setColor(Color.GREEN)\n                    .setPriority(NotificationCompat.PRIORITY_HIGH)\n                    .setChannelId(NOTIFICATION_CHANNEL_ID)\n                    .setAutoCancel(false) // Remove notification on click\n                    .setOngoing(true) // Prevent swipe to remove\n                    //.setContentIntent(pi)\n                    .build()\n\n                startForeground(FOREGROUND_SERVICE_ID, getNotification())\n                startDartIsolate(callbackLibraryPath, callbackName)\n            }\n        } catch (exception: Throwable) {\n            Log.e(TAG, \"[initService] An error occurred while initializing the service\", exception)\n            closeService()\n            throw exception\n        }\n    }\n\n    /// Close background service:\n    /// + Stop foreground service and remove the notification\n    /// + Destroy and discard flutter engine\n    /// + Delete notification channel\n    private fun closeService() {\n        try {\n            try {\n                Log.d(TAG, \"[closeService] Stop foreground service and remove the notification.\")\n                stopForeground(true)\n            } catch (err: Throwable) {\n                Log.w(TAG, \"[closeService] Can't stop foreground service\", err)\n            }\n\n            Log.d(TAG, \"[closeService] Destroy and discard flutter engine.\")\n            flutterEngine?.apply {\n                try {\n                    serviceControlSurface.detachFromService()\n                } catch (err: Throwable) {\n                    Log.w(TAG, \"[closeService] Can't detach service control surface from flutter engine\", err)\n                }\n                try {\n                    plugins.removeAll()\n                } catch (err: Throwable) {\n                    Log.w(TAG, \"[closeService] Can't remove plugins from flutter engine\", err)\n                }\n                destroy()\n            }\n            flutterEngine = null\n\n            try {\n                Log.d(TAG, \"[closeService] Delete notification channel.\")\n                deleteNotificationChannel()\n            } catch (err: Throwable) {\n                Log.w(TAG, \"[closeService] Can't delete notification channel\", err)\n            }\n        } catch (exception: Throwable) {\n            Log.e(TAG, \"[closeService] Error freeing background service resources\", exception)\n            throw exception\n        }\n    }\n\n    /// Create a notification channel for foreground service\n    private fun createNotificationChannel() {\n        if (Build.VERSION.SDK_INT \u003c Build.VERSION_CODES.O) return\n        Log.d(TAG, \"[createNotificationChannel] Creating notification channel\")\n        val channel: NotificationChannel = NotificationChannel(\n            NOTIFICATION_CHANNEL_ID,\n            NOTIFICATION_CHANNEL_NAME,\n            NotificationManager.IMPORTANCE_HIGH // IMPORTANCE_HIGH or IMPORTANCE_DEFAULT or IMPORTANCE_LOW\n        ).apply {\n            description = \"Executing process in background\"\n            enableLights(true) // Notifications posted to this channel should display notification lights\n            lightColor = Color.GREEN // Sets the notification light color for notifications posted to this channel\n        }\n        val manager: NotificationManager = ContextCompat.getSystemService(this, NotificationManager::class.java) ?: return\n        manager.createNotificationChannel(channel)\n        //showToast(this, \"Notification channel created\")\n    }\n\n    /// Delete notification channel\n    private fun deleteNotificationChannel() {\n        if (Build.VERSION.SDK_INT \u003c Build.VERSION_CODES.O) return\n        Log.d(TAG, \"[deleteNotificationChannel] Deleting notification channel\")\n        val manager: NotificationManager = ContextCompat.getSystemService(this, NotificationManager::class.java) ?: return\n        manager.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID)\n    }\n\n    /// Create a new FlutterEngine and execute Dart entrypoint.\n    private fun startDartIsolate(callbackLibraryPath: String, callbackName: String) {\n        try {\n            if (isExecutingDart) {\n                val exception = AssertionError(\n                    \"BackgroundService already running!\"\n                )\n                Log.e(TAG, \"[startDartIsolate] BackgroundService already running!\", exception)\n                throw exception\n            }\n            Log.d(TAG, \"[startDartIsolate] Starting flutter engine for background service\")\n\n            val loader: FlutterLoader = FlutterInjector.instance().flutterLoader()\n\n            /* for (asset in loader.getLookupKeyForAsset(\"flutter_assets\")) {\n                Log.d(TAG, \"Asset: $asset\")\n            } */\n\n            if (!loader.initialized()) {\n                loader.startInitialization(applicationContext)\n                // throw AssertionError(\n                //     \"DartEntrypoint can only be created once a FlutterEngine is created.\"\n                // )\n            }\n\n            applicationContext.let { ctx -\u003e\n                loader.apply {\n                    startInitialization(ctx)\n                    ensureInitializationComplete(ctx, emptyArray\u003cString\u003e())\n                }\n                // Set up the engine.\n                flutterEngine = FlutterEngine(ctx, null, false, false).also {\n                    it.serviceControlSurface.attachToService(this, null, true)\n\n                    // You can pass arguments to the dart entrypoint here.\n                    val args: MutableList\u003cString\u003e = MutableList(0) { \"\" }\n\n                    // Start executing Dart entrypoint.\n                    it.dartExecutor.executeDartEntrypoint(\n                        DartExecutor.DartEntrypoint(\n                            loader.findAppBundlePath(),\n                            callbackLibraryPath,\n                            callbackName\n                        ),\n                        args\n                    )\n                    BackgroundPluginRegistrant.registerWith(it)\n                    // flutterEngine?.dartExecutor?.isolateServiceId\n                    it.addEngineLifecycleListener(BackgroundFlutterEngineLifecycleListener {\n                        Log.d(TAG, \"FlutterEngine has shutdown and will be discarded now.\")\n                        flutterEngine = null\n                    })\n                }\n            }\n        } catch (exception: UnsatisfiedLinkError) {\n            Log.w(TAG, \"[runDartEntryPoint] UnsatisfiedLinkError: After a reboot this may happen for a \" +\n                \"short period and it is ok to ignore then!\", exception)\n        } catch (exception: Throwable) {\n            Log.e(TAG, \"[runDartEntryPoint] Unexpected exception during FlutterEngine initialization\", exception)\n            throw exception\n        }\n    }\n}\n\n/// FlutterEngine lifecycle listener\nprivate class BackgroundFlutterEngineLifecycleListener(private val callback: () -\u003e Unit) : FlutterEngine.EngineLifecycleListener {\n    private companion object {\n        private const val TAG = \"BFELListener\"\n    }\n\n    override fun onPreEngineRestart() {\n        TODO(\"Not yet implemented\")\n    }\n\n    override fun onEngineWillDestroy() {\n        Log.d(TAG, \"[onEngineWillDestroy] FlutterEngine has shutdown\")\n        this.callback()\n    }\n}\n\n/// Helper class for storing and retrieving callback information\n/// of last started Background Service's FlutterEngine\nprivate object BackgroundSharedPreferencesHelper {\n    private const val TAG : String = \"BackgroundSPHelper\"\n    private const val SHARED_PREFERENCES_NAME : String = \"tld.domain.background_service\"\n    private const val CALLBACK_LIBRARY_PATH_KEY : String = \"CALLBACK_LIBRARY_PATH\"\n    private const val CALLBACK_NAME_KEY : String = \"CALLBACK_NAME\"\n\n    /// Get entry point of last started Background Service's FlutterEngine\n    /// Returning \u003cCallbackLibraryPath, CallbackName\u003e\n    @Suppress(\"MemberVisibilityCanBePrivate\", \"unused\")\n    fun getLastCallbackInformation(context: Context): Pair\u003cString, String\u003e?  =\n        context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE).let {\n            val callbackLibraryPath: String = it.getString(CALLBACK_LIBRARY_PATH_KEY, null) ?: return null\n            val callbackName: String = it.getString(CALLBACK_NAME_KEY, null) ?: return null\n            Log.d(TAG, \"Callback information loaded: $callbackLibraryPath, $callbackName\")\n            return Pair(callbackLibraryPath, callbackName)\n        }\n\n    /// Put entry point of last started Background Service's FlutterEngine\n    @Suppress(\"MemberVisibilityCanBePrivate\", \"unused\")\n    fun putLastCallbackInformation(context: Context, callbackLibraryPath: String?, callbackName: String?) =\n        with(context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE).edit()) {\n            if (callbackLibraryPath?.isBlank() != false || callbackName?.isBlank() != false) {\n                remove(CALLBACK_LIBRARY_PATH_KEY)\n                remove(CALLBACK_NAME_KEY)\n                apply()\n                Log.d(TAG, \"Callback information removed\")\n            } else {\n                putString(CALLBACK_LIBRARY_PATH_KEY, callbackLibraryPath)\n                putString(CALLBACK_NAME_KEY, callbackName)\n                apply()\n                Log.d(TAG, \"Callback information saved: $callbackLibraryPath, $callbackName\")\n            }\n        }\n\n    /// Remove entry point of last started Background Service's FlutterEngine\n    @Suppress(\"MemberVisibilityCanBePrivate\", \"unused\")\n    fun removeLastCallbackInformation(context: Context) =\n        putLastCallbackInformation(context, null, null)\n}\n\n/// Register the plugins that should be registered when the FlutterEngine is attached to\n/// the BackgroundService.\n@Keep\nobject BackgroundPluginRegistrant {\n    private const val TAG = \"GeneratedPluginRegistrant\"\n    fun registerWith(flutterEngine: FlutterEngine) {\n        try {\n            flutterEngine.plugins.add(BackgroundPlugin())\n            //flutterEngine.plugins.add(Plugin1())\n            //flutterEngine.plugins.add(Plugin2())\n            //flutterEngine.plugins.add(Plugin3())\n        } catch (e: Exception) {\n            Log.e(\n                TAG,\n                \"Error while registering plugin: ${BackgroundPlugin::class.java.canonicalName}\" +\n                \" with ${flutterEngine::class.java.canonicalName}\",\n                e\n            )\n        }\n    }\n}\n```\n\n---\n\n14. `[background]` Create a BackgroundController to handle the messages from the dart side.\n\n```bash\ncode ./android/src/main/kotlin/tld/domain/background_controller/BackgroundController.kt\n```\n\n\u003c\u003c\n\n```kotlin\npackage tld.domain.background_controller\n\nimport android.content.Context\nimport android.util.Log\nimport tld.domain.background_service.BackgroundService\nimport io.flutter.embedding.engine.plugins.FlutterPlugin\nimport io.flutter.plugin.common.BinaryMessenger\nimport tld.domain.background_controller.api.*\n\n/// A Controller that will be attached to the FlutterEngine and will be responsible for the\n/// communication between Flutter and native Android.\ninterface IAttachableBackgroundController {\n    /// Attach to the FlutterEngine.\n    fun attach()\n\n    /// Detach from the FlutterEngine.\n    fun detach()\n}\n\n/// Communication between Flutter and Android side through method channels.\nclass BackgroundController(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) :\n        IAttachableBackgroundController, ApiFromDart {\n\n    internal companion object {\n        private const val TAG: String = \"BackgroundController\"\n\n        /// This sink should be registered by the [open] method and all the messages should be sent\n        /// through it to the Flutter side which opened the background service.\n        internal var sink: ApiToDart? = null\n    }\n\n    private val applicationContext: Context\n    private val binaryMessenger: BinaryMessenger\n\n    init {\n        flutterPluginBinding.let {\n            applicationContext = it.applicationContext\n            binaryMessenger = it.binaryMessenger\n        }\n    }\n\n    override fun attach() {\n        Log.d(TAG, \"attach\")\n        ApiFromDart.setUp(binaryMessenger, this)\n    }\n\n    override fun detach() {\n        Log.d(TAG, \"detach\")\n        ApiFromDart.setUp(binaryMessenger, null)\n    }\n\n    override fun open(openMessage: OpenMessage, callback: (Result\u003cUnit\u003e) -\u003e Unit) {\n        Log.d(TAG, \"open\")\n        val entryPointRawHandler: Long = openMessage.entryPointRawHandler ?: throw Exception(\"entryPointRawHandler is null\")\n        try {\n            sink = ApiToDart(binaryMessenger)\n            Log.d(TAG, \"startBackgroundService\")\n            BackgroundService.startBackgroundService(applicationContext, entryPointRawHandler)\n            callback(Result.success(Unit))\n            sink?.afterOpening { }\n        } catch (exception: Throwable) {\n            Log.e(TAG, \"Error while starting BackgroundService\", exception)\n            callback(Result.failure(exception))\n        }\n    }\n\n    override fun isOpen(): BooleanValue = BooleanValue(BackgroundService.healthCheck(applicationContext))\n\n    override fun close(callback: (Result\u003cUnit\u003e) -\u003e Unit) {\n        Log.d(TAG, \"close\")\n        try {\n            BackgroundService.stopBackgroundService(applicationContext)\n            callback(Result.success(Unit))\n            sink?.afterClosing { }\n            sink = null\n        } catch (exception: Throwable) {\n            Log.e(TAG, \"Error while closing BackgroundService\", exception)\n            callback(Result.failure(exception))\n        }\n    }\n}\n```\n\n---\n\n15. `[background]` Configure the background plugin.\n\n```bash\ncode ./android/src/main/kotlin/tld/domain/background/BackgroundPlugin.kt\n```\n\n\u003c\u003c\n\n```kotlin\npackage tld.domain.background\n\nimport android.util.Log\nimport io.flutter.embedding.engine.plugins.FlutterPlugin\nimport tld.domain.background_controller.BackgroundController\nimport tld.domain.background_controller.IAttachableBackgroundController\n\n/// BackgroundPlugin - the main plugin class that will be registered with the FlutterEngine\n/// when the FlutterEngine is attached to the Activity.\nclass BackgroundPlugin: FlutterPlugin {\n  companion object {\n    private const val TAG: String = \"BackgroundPlugin\"\n  }\n\n  /// The Controller that will the communication between Flutter and native Android\n  ///\n  /// This local reference serves to register the plugin with the Flutter Engine and unregister it\n  /// when the Flutter Engine is detached from the Activity\n  private var controller: IAttachableBackgroundController? = null\n\n  /// Called when the FlutterEngine is attached to the Activity\n  override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {\n    Log.d(TAG, \"onAttachedToEngine\")\n    controller = BackgroundController(flutterPluginBinding).apply { attach() }\n  }\n\n  /// Called when the FlutterEngine is detached from the Activity\n  override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {\n    Log.d(TAG, \"onDetachedFromEngine\")\n    controller?.detach()\n  }\n}\n```\n\n---\n\n16. `[background]` Configure the auto start after reboot.\n    This receiver should be registered in the `android\\src\\main\\AndroidManifest.xml` file.\n\n```bash\nmkdir ./android/src/main/kotlin/tld/domain/background_boot_receiver\ncode ./android/src/main/kotlin/tld/domain/background_boot_receiver/BackgroundBootReceiver.kt\n```\n\n\u003c\u003c\n\n```kotlin\npackage tld.domain.background_boot_receiver\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.util.Log\nimport android.widget.Toast\nimport tld.domain.background_service.BackgroundService\n\n/// Receives the BOOT_COMPLETED broadcast and starts the BackgroundService\nclass BackgroundBootReceiver : BroadcastReceiver() {\n    internal companion object {\n        private const val TAG: String = \"BackgroundBootReceiver\"\n    }\n\n    override fun onReceive(context: Context?, intent: Intent?) {\n        if (context == null || intent == null || BackgroundService.isExecutingDart) {\n            Toast.makeText(\n                context,\n                \"Can't launch background service with BackgroundBootReceiver\",\n                Toast.LENGTH_LONG\n            ).show()\n            return\n        }\n        when (intent.action) {\n            Intent.ACTION_BOOT_COMPLETED,\n            Intent.ACTION_MY_PACKAGE_REPLACED,\n            \"android.intent.action.QUICKBOOT_POWERON\",\n            \"com.htc.intent.action.QUICKBOOT_POWERON\" -\u003e {\n                Log.d(TAG, \"[onReceive] Start BackgroundService on intent ${intent.action}\")\n                try {\n                    if (!BackgroundService.startBackgroundService(context)) {\n                        Toast.makeText(\n                            context,\n                            \"Can't launch background service\",\n                            Toast.LENGTH_LONG\n                        ).show()\n                    }\n                } catch (exception: Throwable) {\n                    Log.e(TAG, \"Error while starting Background service\", exception)\n                    Toast.makeText(context, \"Error while starting Background service: ${exception.message}\", Toast.LENGTH_SHORT).show()\n                }\n            }\n        }\n    }\n}\n```\n\n---\n\n17. `[background]` Allow the background to use method channels with plugins and close itself.\n\n```bash\ncode lib/src/background_service.dart\n```\n\n\u003c\u003c\n\n```dart\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/scheduler.dart';\nimport 'package:flutter/services.dart';\nimport 'package:meta/meta.dart';\n\nimport 'controller/api.g.dart';\n\n/// Background service, should be initialized in the background entry point.\n@internal\nclass BackgroundService {\n  static final BackgroundService _internalSingleton =\n      BackgroundService._internal();\n\n  // Initialize the background service, bindings and method channels.\n  static BackgroundService get instance =\u003e _internalSingleton;\n  BackgroundService._internal() {\n    _servicesBinding = _$BackgroundBinding();\n  }\n\n  // ignore: unused_field\n  static ServicesBinding? _servicesBinding;\n\n  Future\u003cvoid\u003e close() =\u003e ApiFromDart().close();\n}\n\n/// Binding for the background service,\n/// allows to use the [SchedulerBinding] and\n/// [ServicesBinding] in the background.\n/// For MethodChannel and Plugins to work,\n/// we need to initialize the [ServicesBinding].\nclass _$BackgroundBinding = BindingBase with SchedulerBinding, ServicesBinding;\n```\n\nand update background's `main.dart`\n\n```bash\ncode lib/src/main.dart\n```\n\n\u003c\u003c\n\n```dart\nimport 'dart:async';\nimport 'dart:developer' as developer;\n\nimport 'background_service.dart';\n\n/// Background entry point\n@pragma('vm:entry-point')\nvoid main() =\u003e runZonedGuarded\u003cvoid\u003e(\n      () async {\n        // Initialize the background service, bindings,\n        // method channels, and plugins.\n        BackgroundService.instance;\n        /* Your code goes here... */\n      },\n      (error, stackTrace) =\u003e developer.log(\n        'A global error has occurred: $error',\n        error: error,\n        stackTrace: stackTrace,\n        name: 'background',\n        level: 900,\n      ),\n    );\n\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplugfox%2Fflutter_background_example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplugfox%2Fflutter_background_example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplugfox%2Fflutter_background_example/lists"}