Hello communityš.
We all know that working with location-based services can be challengingš©āfrom managing location accuracy to ensuring efficient background updates, itās a time-consumingā process.
Developers often face issues like::
- Managing accuracy
- Handling background services
- Optimising battery usage
- Real-time background updates
To make this easier for developers, our CanopasTeam has built GroupTrack, an open-source Flutter project aimed at simplifying location-based services and background operations. GroupTrack tackles the common problems developers face.
Let's delve into the specific approaches used in the project.
1ļøā£Ā Managing accuracy
The project utilises theĀ geolocatorĀ package, a popular Flutter plugin for accessing location services. This package allows the app to specify the desired accuracy when requesting location data.
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
distanceFilter: 10, // Update every 10 meters
);
-
Desired Accuracy Levels: The desiredAccuracy parameter can be set to levels likeĀ
high
,Āmedium
, orĀlow
, depending on the needs of the app at that moment. - Impact on Battery Life: Higher accuracy requires more power. By adjusting the accuracy level, the app conserves battery when high precision isn't necessary.
- Reduces Unnecessary Updates: By only receiving updates when the user has moved a certain distance, the app minimizes redundant processing and this helps in reducing CPU usage and conserves battery life.
iOS Implementation:
For iOS, we use native code in the AppDelegate file to manage location services.
var locationManager: CLLocationManager?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
setUpLocation()
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func setUpLocation() {
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.distanceFilter = 10
locationManager?.desiredAccuracy = kCLLocationAccuracyBest
}
2ļøā£ Handling background services
To handle location services in the background, we configure both Android and iOS separately.
Android
Add the following service configuration to your AndroidManifest.xml file:
<service
android:name="id.flutter.flutter_background_service.BackgroundService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="location" />
iOS
Enable Background Mode in the Signing & Capabilities section.
add this in app delegate
private func setUpLocation() {
locationManager?.allowsBackgroundLocationUpdates = true
locationManager?.pausesLocationUpdatesAutomatically = false
locationManager?.startMonitoringSignificantLocationChanges()
}
Flutter
The project utilises theĀ flutter_background_serviceĀ package for managing background service.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
startService();
runApp(
UncontrolledProviderScope(container: container, child: const App()),
);
}
final bgService = FlutterBackgroundService();
void startService() async {
await bgService.configure(
androidConfiguration: AndroidConfiguration(
onStart: onStart,
autoStart: false,
isForegroundMode: true,
),
iosConfiguration: IosConfiguration(
autoStart: false,
onForeground: onStart,
onBackground: onIosBackground,
),
);
final isLocationPermission = await Permission.location.isGranted;
if (isLocationPermission) {
bgService.startService();
}
}
StreamSubscription<Position>? positionSubscription;
Position? _position;
@pragma('vm:entry-point')
Future<void> onStart(ServiceInstance service) async {
WidgetsFlutterBinding.ensureInitialized();
final isLocationPermission = await Permission.location.isGranted;
if (!isLocationPermission) return;
if (service is AndroidServiceInstance) {
service.setForegroundNotificationInfo(
title: "Your space Location",
content: "Location is being tracked",
);
service.setAsForegroundService();
}
_startLocationUpdates()
service.on('stopService').listen((event) {
positionSubscription?.cancel();
service.stopSelf();
});
}
void _startLocationUpdates() {
positionSubscription = Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: LOCATION_UPDATE_DISTANCE,
),
).listen((position) {
_position = position;
});
}
@pragma('vm:entry-point')
bool onIosBackground(ServiceInstance service) {
onStart(service);
return true;
}
Description:
The app starts a background service that tracks the userās location. It maintains this service in the background using flutter_background_service for Android and appropriate iOS configurations.
3ļøā£ Optimising battery usage
We prioritize battery efficiency while conducting background updates in our app.
To achieve this, we request battery optimization permission from users. This allows the app to operate smoothly in the background without being terminated by the system's battery-saving measures.
For Android, obtaining this permission is crucial for uninterrupted background operations.
So you have to keep in mind that some of the devices do not allow direct battery restriction after allowing permission they want to enable manually battery restriction for apps from system settings.
On iOS, we utilize background fetch and location updates to further enhance battery optimization.
4ļøā£Ā Real-time background updates
For real-time location updates, we handle iOS separately to manage background update and battery optimisation
iOS
private func setUpLocation() {
locationManager?.allowsBackgroundLocationUpdates = true
locationManager?.pausesLocationUpdatesAutomatically = false
locationManager?.startMonitoringSignificantLocationChanges()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let currentLocation = locations.last else { return }
let now = Date().timeIntervalSince1970
let lastUpdate = previousLocation?.timestamp.timeIntervalSince1970 ?? 0
let timeDifference = now - lastUpdate
if timeDifference < 60 { return }
previousLocation = currentLocation
let locationData: [String: Any] = [
"latitude": currentLocation.coordinate.latitude,
"longitude": currentLocation.coordinate.longitude,
"timestamp": currentLocation.timestamp.timeIntervalSince1970 * 1000
]
if let controller = window?.rootViewController as? FlutterViewController {
let methodChannel = FlutterMethodChannel(name: "com.app_name/location", binaryMessenger: controller.binaryMessenger)
methodChannel.invokeMethod("onLocationUpdate", arguments: locationData)
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedAlways || status == .authorizedWhenInUse {
locationManager?.startMonitoringSignificantLocationChanges()
} else {
locationManager?.stopUpdatingLocation()
}
}
Flutter side:
const platform = MethodChannel('com.app_name/location');
platform.setMethodCallHandler(_handleLocationUpdates);
Future<void> _handleLocationUpdates(MethodCall call) async {
if (call.method == 'onLocationUpdate') {
final Map<String, dynamic> locationData = Map<String, dynamic>.from(call.arguments);
final double latitude = locationData['latitude'];
final double longitude = locationData['longitude'];
final DateTime timestamp = DateTime.fromMillisecondsSinceEpoch(locationData['timestamp'].toInt());
// perform action required after getting location updates
}
}
For real-time updates in Android, there's no need for native implementation; the geolocator will handle location updates by position that we declare before.
Conclusion
Group Track simplifies location-based services for Flutter developers, making it easy to integrate real-time tracking with secure, efficient, and reliable performance. By addressing common pain points like accuracy, background services, battery optimization, and real-time updates, Group Track streamlines the process, helping developers build better apps faster.
Here is the github repository, for more information check out thisĀ -Ā https://github.com/canopas/group-track-flutter
Top comments (0)