Replace paid flutter_background_geolocation with free flutter_foreground_task
Replace proprietary flutter_background_geolocation (requires paid license for Android release builds) with free flutter_foreground_task package. Background location tracking now uses foreground service with periodic geolocation updates every 30 seconds.
This commit is contained in:
@@ -10,6 +10,7 @@ import '../providers/map_provider.dart';
|
||||
import '../providers/share_provider.dart';
|
||||
import '../services/geo_service.dart';
|
||||
import '../services/background_geo_service.dart';
|
||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
||||
|
||||
class MapScreen extends StatefulWidget {
|
||||
const MapScreen({super.key});
|
||||
@@ -77,6 +78,7 @@ class _MapScreenState extends State<MapScreen> {
|
||||
token: authProvider.token,
|
||||
geoId: shareProvider.geoId,
|
||||
);
|
||||
FlutterForegroundTask.setTaskHandler(BackgroundTaskHandler());
|
||||
await bgGeo.start();
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
|
||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../config/api.dart';
|
||||
|
||||
@@ -19,11 +19,10 @@ class BackgroundGeoService {
|
||||
String? _token;
|
||||
String? _geoId;
|
||||
bool _isReady = false;
|
||||
bool _isStarted = false;
|
||||
bool _notificationsInitialized = false;
|
||||
|
||||
bool get isReady => _isReady;
|
||||
bool get isStarted => _isStarted;
|
||||
Future<bool> get isStarted async => await FlutterForegroundTask.isRunningService;
|
||||
String? get token => _token;
|
||||
String? get geoId => _geoId;
|
||||
|
||||
@@ -33,11 +32,7 @@ class BackgroundGeoService {
|
||||
const androidSettings = AndroidInitializationSettings(
|
||||
'@mipmap/ic_launcher',
|
||||
);
|
||||
const iosSettings = DarwinInitializationSettings(
|
||||
requestAlertPermission: true,
|
||||
requestBadgePermission: true,
|
||||
requestSoundPermission: true,
|
||||
);
|
||||
const iosSettings = DarwinInitializationSettings();
|
||||
const initSettings = InitializationSettings(
|
||||
android: androidSettings,
|
||||
iOS: iosSettings,
|
||||
@@ -61,69 +56,59 @@ class BackgroundGeoService {
|
||||
|
||||
await initNotifications();
|
||||
|
||||
bg.BackgroundGeolocation.onLocation(_onLocation, _onLocationError);
|
||||
bg.BackgroundGeolocation.onMotionChange(_onMotionChange);
|
||||
bg.BackgroundGeolocation.onProviderChange(_onProviderChange);
|
||||
bg.BackgroundGeolocation.onHttp(_onHttp);
|
||||
FlutterForegroundTask.initCommunicationPort();
|
||||
|
||||
await bg.BackgroundGeolocation.ready(bg.Config(
|
||||
desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH,
|
||||
distanceFilter: 10.0,
|
||||
stopOnTerminate: false,
|
||||
startOnBoot: true,
|
||||
debug: false,
|
||||
logLevel: bg.Config.LOG_LEVEL_OFF,
|
||||
autoSync: false,
|
||||
));
|
||||
FlutterForegroundTask.addTaskDataCallback(_onTaskData);
|
||||
|
||||
_isReady = true;
|
||||
}
|
||||
|
||||
Future<void> start() async {
|
||||
if (!_isReady) return;
|
||||
if (_isStarted) return;
|
||||
if (_token == null || _geoId == null || _geoId!.isEmpty) return;
|
||||
|
||||
await bg.BackgroundGeolocation.start();
|
||||
_isStarted = true;
|
||||
final androidOptions = AndroidNotificationOptions(
|
||||
channelId: 'background_geo_channel',
|
||||
channelName: 'Background Geolocation',
|
||||
channelDescription: 'Отслеживание позиции в фоновом режиме',
|
||||
channelImportance: NotificationChannelImportance.HIGH,
|
||||
priority: NotificationPriority.HIGH,
|
||||
);
|
||||
|
||||
final iosOptions = IOSNotificationOptions(
|
||||
showNotification: true,
|
||||
playSound: true,
|
||||
);
|
||||
|
||||
final taskOptions = ForegroundTaskOptions(
|
||||
eventAction: ForegroundTaskEventAction.repeat(15000),
|
||||
autoRunOnBoot: true,
|
||||
allowWakeLock: true,
|
||||
);
|
||||
|
||||
FlutterForegroundTask.init(
|
||||
androidNotificationOptions: androidOptions,
|
||||
iosNotificationOptions: iosOptions,
|
||||
foregroundTaskOptions: taskOptions,
|
||||
);
|
||||
|
||||
await FlutterForegroundTask.startService(
|
||||
notificationTitle: 'Family Safety',
|
||||
notificationText: 'Отслеживание позиции активно',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
if (!_isStarted) return;
|
||||
await bg.BackgroundGeolocation.stop();
|
||||
_isStarted = false;
|
||||
await FlutterForegroundTask.stopService();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
bg.BackgroundGeolocation.removeListeners();
|
||||
_isReady = false;
|
||||
_isStarted = false;
|
||||
_token = null;
|
||||
_geoId = null;
|
||||
}
|
||||
|
||||
void _onLocation(bg.Location location) {
|
||||
if (_token == null || _geoId == null || _geoId!.isEmpty) return;
|
||||
if (location.sample == true) return;
|
||||
|
||||
_sendPosition(location.coords.latitude, location.coords.longitude);
|
||||
}
|
||||
|
||||
void _onLocationError(bg.LocationError error) {
|
||||
_showErrorNotification('Ошибка геолокации: $error');
|
||||
}
|
||||
|
||||
void _onMotionChange(bg.Location location) {
|
||||
kDebugMode? print('[BackgroundGeo] Motion changed: isMoving=${location.isMoving}'):{};
|
||||
}
|
||||
|
||||
void _onProviderChange(bg.ProviderChangeEvent event) {
|
||||
kDebugMode?print('[BackgroundGeo] Provider changed: $event'):{};
|
||||
}
|
||||
|
||||
void _onHttp(bg.HttpEvent event) {
|
||||
kDebugMode? print('[BackgroundGeo] HTTP: status=${event.status}, success=${event.success}'):{};
|
||||
}
|
||||
static void _onTaskData(Object data) {}
|
||||
|
||||
Future<void> _sendPosition(double latitude, double longitude) async {
|
||||
if (_token == null || _geoId == null || _geoId!.isEmpty) return;
|
||||
@@ -162,10 +147,7 @@ class BackgroundGeoService {
|
||||
priority: Priority.high,
|
||||
ticker: 'geo_error',
|
||||
);
|
||||
const iosDetails = DarwinNotificationDetails(
|
||||
presentAlert: true,
|
||||
sound: 'default',
|
||||
);
|
||||
const iosDetails = DarwinNotificationDetails();
|
||||
const details = NotificationDetails(
|
||||
android: androidDetails,
|
||||
iOS: iosDetails,
|
||||
@@ -184,3 +166,45 @@ class BackgroundGeoService {
|
||||
print('[Notification] ${response.payload}');
|
||||
}
|
||||
}
|
||||
|
||||
class BackgroundTaskHandler extends TaskHandler {
|
||||
int _locationCount = 0;
|
||||
|
||||
@override
|
||||
Future<void> onStart(DateTime timestamp, TaskStarter starter) async {}
|
||||
|
||||
@override
|
||||
void onRepeatEvent(DateTime timestamp) {
|
||||
_locationCount++;
|
||||
|
||||
if (_locationCount % 2 != 0) return;
|
||||
|
||||
_getLocationAndSend();
|
||||
}
|
||||
|
||||
@override
|
||||
void onReceiveData(Object data) {}
|
||||
|
||||
@override
|
||||
void onNotificationButtonPressed(String id) {}
|
||||
|
||||
@override
|
||||
void onNotificationPressed() {}
|
||||
|
||||
@override
|
||||
void onNotificationDismissed() {}
|
||||
|
||||
@override
|
||||
Future<void> onDestroy(DateTime timestamp) async {}
|
||||
|
||||
Future<void> _getLocationAndSend() async {
|
||||
try {
|
||||
final position = await Geolocator.getCurrentPosition();
|
||||
|
||||
final bgGeo = BackgroundGeoService();
|
||||
await bgGeo._sendPosition(position.latitude, position.longitude);
|
||||
} catch (e) {
|
||||
print('[BackgroundGeo] Error getting location: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user