Skip to content

[Bug]: Location Tracking not working at terminated state #1654

@mohadmed-adel

Description

@mohadmed-adel

Required Reading

  • Confirmed

Plugin Version

5.0.5

Flutter Doctor

[✓] Flutter (Channel stable, 3.32.4, on macOS 26.3 25D125 darwin-arm64, locale en-EG)
[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 26.3)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2025.3)
[✓] Connected device (2 available)
    ! Error: Browsing on the local area network for Mohamed’s iPhone. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
[✓] Network resources

• No issues found!

Mobile operating-system(s)

  • iOS
  • Android

Device Manufacturer(s) and Model(s)

iphon 12 and oppo reno 5

Device operating-systems(s)

Android 13 , IOS 18

What happened?

Enhanced Bug Description:

Title: Geofence events not triggered in Release mode after force closing the app

Description:
When the application is force closed and the user exits or enters the configured geofence area, no geofence event is triggered from our side in Release mode.

During testing in Debug mode, the functionality works as expected on both the simulator and emulator, and geofence events are correctly detected. However, after building the application in Release mode, the geofence enter/exit events are not being received or processed once the app is force closed.

Steps to Reproduce:

Install the Release build of the application.

Open the app and ensure the geofence is properly registered.

Force close the application.

Move outside or inside the configured geofence area.

Observe that no geofence event is triggered.

Expected Result:
The application should detect geofence enter/exit events even when the app is force closed, similar to the behavior observed in Debug mode.

Actual Result:
No geofence events are triggered or received in Release mode after the app is force closed.

Environment:

Tested on Simulator & Emulator

Works in Debug mode

Issue occurs in Release mode

Notes / Additional Context:

Geofence functionality appears to stop working only after the app is force closed in Release builds.

Further investigation may be required regarding background execution permissions, OS restrictions, or release build configuration.

Plugin Code and/or Config

part of 'index.dart';

/// Handles background geolocation configuration for geofence operations.
///
/// Must be called on every app launch so that [BackgroundGeolocation.ready]
/// runs each time (required for iOS geofencing after force kill).
/// For debugging: use debug: true, logLevel: LogLevel.verbose and fetch
/// plugin logs via [Logger.emailLog].
class GeofenceConfig {
  /// Applies plugin config. Call on every app launch (e.g. from main).
  static Future<void> updateGeofenceHttpConfig() async {
    String token =
        storageService.valueGetter(key: StorageKeys.token, defualt: "");

    String? userId = getUserIdFromToken(token);

    // Configure the plugin with v5 compound config
    // Note: Working hours are enforced via heartbeat checks instead of schedule
    await bg.BackgroundGeolocation.ready(
      bg.Config(
        geolocation: const bg.GeoConfig(
          locationAuthorizationRequest: 'Always',
          disableLocationAuthorizationAlert: false,
          desiredAccuracy: bg.DesiredAccuracy.high,
          distanceFilter: 150,
          disableElasticity: true,
          geofenceModeHighAccuracy: true,
          showsBackgroundLocationIndicator: true,
          pausesLocationUpdatesAutomatically: false,
          stationaryRadius: 200,
          locationUpdateInterval: 600000,
        ),
        app: const bg.AppConfig(
          stopOnTerminate: false,
          startOnBoot: true,
          preventSuspend: true,
          enableHeadless: true,
          schedule: [],
          heartbeatInterval: 60,
        ),
        http: bg.HttpConfig(
          autoSync: true,
        ),
        persistence: bg.PersistenceConfig(
          extras: {'emp_code': userId},
        ),
        logger: const bg.LoggerConfig(
          debug: foundation.kDebugMode,
          logLevel: bg.LogLevel.verbose,
        ),
        activity: const bg.ActivityConfig(
          disableMotionActivityUpdates: false,
          stopOnStationary: false,
        ),
        isMoving: true,
        foregroundService: true,
        notification: bg.Notification(
          smallIcon: '@mipmap/launcher_icon',
          largeIcon: '@mipmap/launcher_icon',
          title: 'Aictime',
          text: 'Location Tracking',
          color: '@mipmap/launcher_icon',
          channelId: 'geofence_service',
          channelName: 'Geofence Service',
        ),
      ),
    );
  }
}

// main.dart 
import 'dart:io';

import 'package:aictime/base/data/local_auth_serveices.dart';
import 'package:aictime/base/data/notifications_services.dart';
import 'package:aictime/base/data/punch_retry_queue.dart';
import 'package:aictime/base/data/storage_services.dart';
import 'package:aictime/common/time/time_validation_result.dart';
import 'package:aictime/features/checkInternetConnection/repo/check_internet_modal_view.dart';
import 'package:aictime/firebase_options.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart'
    as bg;
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'base/app/Application.dart';
import 'base/data/api_services.dart';
import 'base/geofence/index.dart';

class MyHttpOverrides extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext? context) {
    return super.createHttpClient(context)
      ..badCertificateCallback =
          (X509Certificate cert, String host, int port) => true;
  }
}

void main() async {
  HttpOverrides.global = MyHttpOverrides();
  WidgetsFlutterBinding.ensureInitialized();

  // Set up global error handlers to log all errors to API
  _setupGlobalErrorHandlers();

  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  storageService = await StorageService.init();

  // Initialize notifications first
  await GlobalNotification.instance.setUpNotification();

  // Check if face recognition is available
  List res = await LocalAuth().getAvailableBiometrics();
  if (res.isEmpty) {
    storageService.valueSetter(key: StorageKeys.isBioEnable, value: false);
  } else {
    storageService.valueSetter(key: StorageKeys.isBioEnable, value: true);
  }

  // enable or disable LicenseIntegration
  storageService.valueSetter(
    key: StorageKeys.enableLicenseIntegration,
    value: false,
  );
  setAppDebugMode(storageService);
  ConnectionStatusSingleton connectionStatus =
      ConnectionStatusSingleton.getInstance();
  connectionStatus.initialize();
  ApiRequestService().init();
  try {
    // Initialize punch retry queue
    await PunchRetryQueue().init();
  } catch (e) {
    debugPrint("error in PunchRetryQueue init: $e");
  }

  // Apply BackgroundGeolocation.ready() on every launch (required for iOS geofencing
  // after force kill — see https://github.com/transistorsoft/flutter_background_geolocation/issues/1650)
  try {
    await GeofenceConfig.updateGeofenceHttpConfig();
  } catch (e) {
    debugPrint("GeofenceConfig on launch: $e");
  }
  // Register headless task so the plugin can call it when app is terminated
  // (e.g. geofence ENTER/EXIT or provider change). Required when enableHeadless: true.
  bg.BackgroundGeolocation.registerHeadlessTask(
      backgroundGeolocationHeadlessTask);
  // Initialize time validator
  TimeValidator().init();

  runApp(const ProviderScope(child: Application()));
}

/// Sets up global error handlers to catch and log all errors
void _setupGlobalErrorHandlers() {
  // Handle Flutter framework errors (widget errors, etc.)
  FlutterError.onError = (FlutterErrorDetails details) {
    // FlutterError.presentError(details);
    // Log to API endpoint
  };

  // Handle async errors that occur outside of Flutter framework
  PlatformDispatcher.instance.onError = (error, stack) {
    // Log to API endpoint

    return true; // Return true to indicate error was handled
  };
}

Relevant log output

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions