skip to Main Content

I am creating a Flutter app that requires fetching the location of the user when the app is closed with a frequency of around 15-30 seconds. Currently, I use the ‘location’ flutter package which allows me to fetch the location when the app is open but not on screen. However, I want the location to be tracked when I close the app, and also turn on the phone.

I have tried using geolocator and workmanager to retrieve the location, but those have minimum frequencies of 15 minutes, which is far too infrequent. I currently use the ‘location’ package, however it stops running when the app is closed and does not run on startup. I have found flutter_background_geolocation, however it is not open source.

This is my implementation:

myapp/android/app/main/java/com/flutter/myapp/LocationService.kt

package com.flutter.myapp

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.FusedLocationProviderClient
class LocationService : Service() {
    private val CHANNEL_ID = "ForegroundServiceChannel"
    private lateinit var fusedLocationClient: FusedLocationProviderClient
    private lateinit var locationCallback: LocationCallback

    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                super.onLocationResult(locationResult)
                locationResult?.let {
                    // Handle location update here
                    val location = it.lastLocation
            }
        }
        startLocationUpdates()
    }
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val serviceChannel = NotificationChannel(
                CHANNEL_ID,
                "Foreground Service Channel",
                NotificationManager.IMPORTANCE_DEFAULT
            )
            val manager = getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(serviceChannel)
        }
    }

    private fun startLocationUpdates() {
        val locationRequest = LocationRequest.create().apply {
            interval = 15000 
            fastestInterval = 5000
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }

        fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null)
    }
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Location Service")
            .setContentText("Tracking your location...")
            .setContentIntent(getPendingIntent())
            .build()

        startForeground(1, notification)
        return START_STICKY
    }
    private fun getPendingIntent(): PendingIntent {
        val notificationIntent = Intent(this, MainActivity::class.java)
        return PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
    }
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
    override fun onDestroy() {
        super.onDestroy()
        fusedLocationClient.removeLocationUpdates(locationCallback)
    }
}

Addition to myapp/android/app/main/kotlin/com/flutter/myapp/MainActivity.kt:

<service
        android:name=".LocationService"
        android:enabled="true"
        android:exported="false"/>

Usage of service:

class ForegroundService {
  static const platform =
      MethodChannel('com.flutter.myapp/locationService');

  Future<void> startService() async {
    try {
      final intent = AndroidIntent(
        action: 'com.flutter.myapp.START_LOCATION_SERVICE',
        package: 'com.flutter.myapp',
        componentName: 'com.flutter.myapp/.LocationService',
        flags: <int>[Flag.FLAG_ACTIVITY_NEW_TASK],
      ); 
      intent.sendBroadcast();
      await platform.invokeMethod('startService');
    } on PlatformException catch (e) {
      print("Failed to start service: '${e.message}'.");
    }
  }

  Future<void> stopService() async {
    try {
      await platform.invokeMethod('stopService');
    } on PlatformException catch (e) {
      print("Failed to stop service: '${e.message}'.");
    }
  }

When I close the app (i.e. swiping up in the list of apps), this is printed in the logs:

D/FlutterGeolocator(27222): Detaching Geolocator from activity
D/FlutterGeolocator(27222): Flutter engine disconnected. Connected engine count 0
D/FlutterGeolocator(27222): Disposing Geolocator services
E/FlutterGeolocator(27222): Geolocator position updates stopped
D/FlutterGeolocator(27222): Stopping location service.
D/FlutterLocationService(27222): Unbinding from location service.
D/FlutterLocationService(27222): Destroying service.
D/FlutterGeolocator(27222): Unbinding from location service.
D/FlutterGeolocator(27222): Destroying location service.
D/FlutterGeolocator(27222): Stopping location service.
D/FlutterGeolocator(27222): Destroyed location service.

2

Answers


  1. There is the package background locator that might do the trick.
    Please, check this issue, hope it helps.

    Login or Signup to reply.
  2. flutter_background_service helped in my case.
    There documentation explains most of the things

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search