flutter_background_geolocation icon indicating copy to clipboard operation
flutter_background_geolocation copied to clipboard

[Help Wanted]: Odometer value does not update when the app is terminated.

Open amit98-Flutter opened this issue 8 months ago • 7 comments

Required Reading

  • [x] Confirmed

Plugin Version

flutter_background_geolocation: ^4.16.9

Mobile operating-system(s)

  • [ ] iOS
  • [x] Android

Device Manufacturer(s) and Model(s)

Samsung Galaxy A12

Device operating-systems(s)

Android 13

What do you require assistance about?

I'm currently sending the final odometer value to the server when a task is completed. However, I'm facing an issue where the odometer value resets to zero if the app is terminated and restarted. This leads to inaccurate distance tracking for the user. Could you help me identify what I might be missing or suggest how to persist the odometer value across app restarts?

[Optional] Plugin Code and/or Config

// Add this function outside main()
final log = logger.Logger();

// Headless task for background location updates
@pragma('vm:entry-point')
void backgroundGeolocationHeadlessTask(bg.HeadlessEvent event) async {
  try {
    log.i('[HeadlessTask] Event: ${event.name}');

    switch (event.name) {
      case bg.Event.LOCATION:
        final location = event.event;        
        break;

      case bg.Event.HEARTBEAT:
        await bg.BackgroundGeolocation.sync();
        break;

      case bg.Event.TERMINATE:
        break;
    }
  } catch (e, st) {
    log.e('Headless task error', error: e, stackTrace: st);
  }
}



void main() async {
  WidgetsFlutterBinding.ensureInitialized();
 GestureBinding.instance.resamplingEnabled = true;


  bg.BackgroundGeolocation.ready(bg.Config(
          reset: true,
          debug: true,
          preventSuspend: true,
          logLevel: bg.Config.LOG_LEVEL_VERBOSE,
          desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH,
          distanceFilter: 10.0,
          heartbeatInterval: 60,
          disableElasticity: true,
          isMoving: true,
          locationUpdateInterval: 15000,
          fastestLocationUpdateInterval: 10000,
          deferTime: 0,
          forceReloadOnBoot: true,
          forceReloadOnGeofence: true,
          forceReloadOnHeartbeat: true,
          pausesLocationUpdatesAutomatically: false,
          showsBackgroundLocationIndicator: true,
          useSignificantChangesOnly: false,
          activityRecognitionInterval: 1000,
          backgroundPermissionRationale: bg.PermissionRationale(
              title: "Allow ${dotenv.get('APP_NAME')} to access this device's location even when the app is closed or not in use.",
              message: "This app collects location data to enable recording your trips to work and calculate distance-travelled.",
              positiveAction: 'Change to "{backgroundPermissionOptionLabel}"',
              negativeAction: 'Cancel'),
          stopOnTerminate: false,
          locationAuthorizationRequest: 'Always',
          persistMode: 1,
          autoSync: true,
          startOnBoot: true,
          enableHeadless: true))
      .then((bg.State state) async {
    print("[ready] ${state.toMap()}");
    if (state.schedule!.isNotEmpty) {
      bg.BackgroundGeolocation.startSchedule();
    }
    if (state.enabled) {}
  }).catchError((error) {
    print('[ready] ERROR: $error');
  });

  // Register headless task
  bg.BackgroundGeolocation.registerHeadlessTask(backgroundGeolocationHeadlessTask);


  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]).then((_) {
    runApp(const App());
  });
}


startTaskTracking(String taskId) async {
  bg.State state = await bg.BackgroundGeolocation.state;
  if (!state.enabled) {
    await bg.TransistorAuthorizationToken.destroy(dotenv.get('LOCATION_BASE_URL'));
    await bg.BackgroundGeolocation.setConfig(bg.Config(
        transistorAuthorizationToken: await bg.TransistorAuthorizationToken.findOrCreate("Beta24", taskId, dotenv.get('LOCATION_BASE_URL')),
        desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH,
        foregroundService: true,
        notification: bg.Notification(
            title: "Task Location Tracking",
            text: "Tracking your location for accurate distance calculation",
            channelName: "Task Tracking",
            sticky: true,
            priority: bg.Config.NOTIFICATION_PRIORITY_MAX)));
    bg.BackgroundGeolocation.setOdometer(0.0);

    await bg.BackgroundGeolocation.strat();
    CommonFunctions.showSuccessToast("Location Tracking Started");

    // Force a location update immediately
    await bg.BackgroundGeolocation.getCurrentPosition(persist: true, desiredAccuracy: 0, timeout: 30000, samples: 3)
        .then((bg.Location location) {
      debugPrint("[getCurrentPosition] ${location.toMap()}");
    }).catchError((error) {
      debugPrint("[getCurrentPosition] ERROR: $error");
    });
  }
}

Future<void> stopTaskTracking() async {
  await bg.BackgroundGeolocation.stop();
  await bg.TransistorAuthorizationToken.destroy(dotenv.get('LOCATION_BASE_URL'));
  bg.BackgroundGeolocation.destroyLocations();
  bg.BackgroundGeolocation.removeListeners();
  bg.BackgroundGeolocation.setOdometer(0.0);
}

[Optional] Relevant log output


amit98-Flutter avatar May 17 '25 10:05 amit98-Flutter

If I were you, I’d disable everywhere that . setOdometer(0.0) is called, to determine if that is the source of my problem.

On an unrelated note, where did you get the idea to use these options?

forceReloadOnBoot: true, forceReloadOnGeofence: true, forceReloadOnHeartbeat: true,

christocracy avatar May 17 '25 12:05 christocracy

If I remove the specified methods, is it possible that background location updates will start working correctly? I'm trying to determine whether those implementations are interfering with background tracking.

Updated -----

@pragma('vm:entry-point') void backgroundGeolocationHeadlessTask(bg.HeadlessEvent event) async { try { log.i('[HeadlessTask] Event: ${event.name}');

switch (event.name) {
  case bg.Event.LOCATION:
    final location = event.event;
    HydratedBloc.storage.write("odometer", location.odometer);
    log.f("LOCATION UPDATE ---> $location");
    break;
  case bg.Event.HEARTBEAT:
    await bg.BackgroundGeolocation.sync();
    break;

  case bg.Event.TERMINATE:
    break;
}

} catch (e, st) { log.e('Headless task error', error: e, stackTrace: st); } }

bg.BackgroundGeolocation.ready(bg.Config( reset: true, debug: true, preventSuspend: true, logLevel: bg.Config.LOG_LEVEL_VERBOSE, desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH, distanceFilter: 10.0, heartbeatInterval: 60, disableElasticity: true, isMoving: true, locationUpdateInterval: 15000, fastestLocationUpdateInterval: 10000, deferTime: 0, pausesLocationUpdatesAutomatically: false, showsBackgroundLocationIndicator: true, backgroundPermissionRationale: bg.PermissionRationale( title: "Allow ${dotenv.get('APP_NAME')} to access this device's location even when the app is closed or not in use.", message: "This app collects location data to enable recording your trips to work and calculate distance-travelled.", positiveAction: 'Change to "{backgroundPermissionOptionLabel}"', negativeAction: 'Cancel'), stopOnTerminate: false, locationAuthorizationRequest: 'Always', persistMode: 1, autoSync: true, startOnBoot: true, enableHeadless: true)) .then((bg.State state) async { print("[ready] ${state.toMap()}"); if (state.schedule!.isNotEmpty) { bg.BackgroundGeolocation.startSchedule(); } if (state.enabled) { bg.BackgroundGeolocation.setOdometer(HydratedBloc.storage.read("odometer")); } }).catchError((error) { print('[ready] ERROR: $error'); });

// Register headless task
bg.BackgroundGeolocation.registerHeadlessTask(backgroundGeolocationHeadlessTask);

amit98-Flutter avatar May 17 '25 12:05 amit98-Flutter

Read what I said again.

Also, I asked a question. Please answer my question.

christocracy avatar May 17 '25 12:05 christocracy

I'm reviewing the plugin documentation and making the necessary changes in hopes that it will work even when the app is terminated.

amit98-Flutter avatar May 17 '25 12:05 amit98-Flutter

I asked you a question. Please answer it.

On an unrelated note, where did you get the idea to use these options?

forceReloadOnBoot: true, forceReloadOnGeofence: true, forceReloadOnHeartbeat: true,

I also said:

If I were you, I’d disable everywhere that . setOdometer(0.0) is called, to determine if that is the source of my problem.

COMMENT THOSE LINES OUT AND TEST. I didn’t say change those lines and ask more questions.. I said remove those lines

christocracy avatar May 17 '25 12:05 christocracy

bg.BackgroundGeolocation.setOdometer(HydratedBloc.storage.read("odometer"))

That would not be a smart thing to do. The plug-in already stores its odometer in persistent storage.

christocracy avatar May 17 '25 20:05 christocracy

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] avatar Jun 17 '25 02:06 github-actions[bot]

This issue was closed because it has been inactive for 14 days since being marked as stale.

github-actions[bot] avatar Jul 01 '25 02:07 github-actions[bot]