-
Notifications
You must be signed in to change notification settings - Fork 281
[Bug]: Location Tracking not working at terminated state #1654
Description
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
};
}