Basel-525k / geofence_foreground_service

Apache License 2.0
8 stars 7 forks source link

geofence_foreground_service

Flutter Version License Platform Platform

A Flutter plugin that enables you to easily handle geofencing events in your Flutter app by utilizing native OS APIs on Android by creating a foreground service while being battery efficient since it uses the Geofence and WorkManager APIs. And on iOS by utilizing the CLLocationManager

It's important to note that the workmanager and flutter_foreground_task plugins were a great source of inspiration while creating this plugin.

Android iOS
Android Demo iOS Demo

Features

Setup

🔧 Android Setup

<service 
    android:name="com.f2fk.geofence_foreground_service.GeofenceForegroundService"
    android:foregroundServiceType="location">
</service>
- Make sure the `minSdkVersion` in the `app/build.gradle` file is 29+

### 🔧 iOS Setup

- Navigate to the Podfile and make sure to set the iOS version to 12+

platform :ios, '12.0'

- Make sure to add the following permission to your Info.plist
NSLocationAlwaysAndWhenInUseUsageDescription This app need your location to provide best feature based on location NSLocationAlwaysUsageDescription This app need your location to provide best feature based on location NSLocationWhenInUseUsageDescription This app need your location to provide best feature based on location
- Turn on the `Location updates` and `Background fetch` capabilities from XCode
  ![iOS capabilities](https://github.com/Basel-525k/geofence_foreground_service/blob/main/assets/images/ios_setup_steps.png?raw=true)

## Example

Define the method that will handle the Geofence triggers

```dart
import 'package:geofence_foreground_service/exports.dart';
import 'package:geofence_foreground_service/geofence_foreground_service.dart';
import 'package:geofence_foreground_service/models/zone.dart';

// This method is a top level method
@pragma('vm:entry-point')
void callbackDispatcher() async {
  GeofenceForegroundService().handleTrigger(
    backgroundTriggerHandler: (zoneID, triggerType) {
      log(zoneID, name: 'zoneID');

      if (triggerType == GeofenceEventType.enter) {
        log('enter', name: 'triggerType');
      } else if (triggerType == GeofenceEventType.exit) {
        log('exit', name: 'triggerType');
      } else if (triggerType == GeofenceEventType.dwell) {
        log('dwell', name: 'triggerType');
      } else {
        log('unknown', name: 'triggerType');
      }

      return Future.value(true);
    },
  );
}

Then create an instance of the plugin to initiate it and assign GeoFences to it

final List<LatLng> timesSquarePolygon = [
  const LatLng(40.758078, -73.985640),
  const LatLng(40.757983, -73.985417),
  const LatLng(40.757881, -73.985493),
  const LatLng(40.757956, -73.985688),
];

Future<void> initPlatformState() async {
  // Remember to handle permissions before initiating the plugin

  bool hasServiceStarted = await GeofenceForegroundService().startGeofencingService(
    contentTitle: 'Test app is running in the background',
    contentText: 'Test app will be running to ensure seamless integration with ops team',
    notificationChannelId: 'com.app.geofencing_notifications_channel',
    serviceId: 525600,
    callbackDispatcher: callbackDispatcher,
  );

  if (hasServiceStarted) {
    await GeofenceForegroundService().addGeofenceZone(
      zone: Zone(
        id: 'zone#1_id',
        radius: 10000, // measured in meters
        coordinates: timesSquarePolygon,
      ),
    );
  }
}

Something important to point out is the callbackDispatcher method will run in an entirely different isolate than the actual app, so if you were to handle UI related code inside of it you'll need to use Ports, you can find more information here

You can pass a custom icon to the foreground service notification if you wish while initializing the service, this icon will be placed inside the android/app/src/main/res folder, you can check the example for more information, by default, it will take the app icon

const NotificationIconData(
  resType: ResourceType.mipmap,
  resPrefix: ResourcePrefix.ic,
  name: 'launcher',
)

Notes

Handling permissions is not a part of the package, so please refer to permission_handler plugin to grant the required permissions (it's used in the example too)

  • location
  • locationAlways
  • notification

Contributing Guidelines

We welcome contributions from the community. If you'd like to contribute to the development of this plugin, please feel free to submit a PR to our GitHub repository._