betracky_background_location 1.0.3
betracky_background_location: ^1.0.3 copied to clipboard
Betracky Background Location Service with Upload Locations to Your Own Server
BeTracky Background Location Service #
BeTracky Background Location Service is a Flutter package that provides background location tracking functionality. It can use to track the device's location even when the app is not in the foreground.
Features #
- Background location tracking
- Configurable distance filter and accuracy
- Start on boot option
- Foreground service support
- Offline mode to store locations locally when offline and upload them in batches when back online
- Upload locations to a custom endpoint
- Upload locations to BeTracky API
- Custom location id support
Getting started #
To use this package, add betracky_background_location as a dependency in your pubspec.yaml file:
Usage #
-
Import the package
-
You can send
urlandaccess_tokenif you have your own backend endpoint to upload locations. This will help you to upload locations even when the app is closed. -
The
offlineEnabledmode allows the app to store locations locally when offline and upload them in batches when back online. -
The
urlandaccess_tokenandidare not mandatory. If you do not use a token, just pass theurl. If you use a token, it should be a Bearer token. If you pass an id it will be sent to the endpoint as the location id otherwise it will generate id.(user_id, device_id, journey_id etc) -
Start the service by calling the
startServicemethod. You can pass the following parameters:distanceFilter: The minimum distance between location updates in meters. Default is 0.accuracy: The desired accuracy for location updates. Default isLocationAccuracy.high.startOnBoot: Whether to start the service when the device boots up. Default isfalse.foregroundService: Whether to run the service as a foreground service. Default isfalse.offlineEnabled: Whether to store locations locally when offline and upload them in batches when back online. Default isfalse.url: The endpoint URL to upload locations. Optional.access_token: The access token to authenticate with the endpoint. Optional.id: The location id to send to the endpoint. Optional.betrackyToken: The token to authenticate with BeTracky API. This is mandatory.
import 'package:betracky_background_location/betracky_background_location.dart';
try {
BeTrackyBackgroundLocation.startService(
distanceFilter: 0,
accuracy: LocationAccuracy.high,
startOnBoot: true,
foregroundService: true,
url: your_endpoint, // Optional
access_token: your_endpoint_access_token, // Optional
id: 5, // Optional - This is the location id that will be sent to the endpoint
offlineEnabled: true, // Optional
betrackyToken: "e6fac1fd-1bc0-449b-a562-22b9b916e3098jhA",
);
} catch (e) {
print('ErrorBetracky: $e');
}
- Stop the service by calling the
stopServicemethod.
BeTrackyBackgroundLocation.stopService();
- If you enabled online mode, this is the format your endpoint should accept for the payload:
l_id,latitude,longitude,actual_created_timeare the fields that will be sent to the endpoint.
[
{
"l_id": 5,
"latitude": 12.345678,
"longitude": 98.765432,
"actual_created_time": "2023-10-10T10:10:10.000Z"
}
]
- If offline mode is enabled, the app will upload 50 bulk locations in the following format:
[
{
"l_id": 5,
"latitude": 12.345678,
"longitude": 98.765432,
"actual_created_time": "2023-10-10T10:10:10.000Z"
},
{
"l_id": 5,
"latitude": 12.345678,
"longitude": 98.765432,
"actual_created_time": "2023-10-10T10:10:10.000Z"
},
...
]
- Install permission_handler and geolocator packages to request location permissions and get the device's location.
dependencies:
permission_handler: latest_version
geolocator: latest_version
- You need to add following permissions in your
AndroidManifest.xmlfile:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<!-- Inside <application>-->
<application
<!-- Add this service-->
<service
android:name="com.transistorsoft.flutter.backgroundfetch.HeadlessTask"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"/>
</application>
- You need to add following permissions in your
Info.plistfile:
<key>NSLocationAlwaysUsageDescription</key>
<string>Location is required to track your location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Location is required to track your location</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Location is required to track your location</string>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>location</string>
</array>
Example #
import 'dart:async';
import 'dart:convert';
import 'package:betracky_background_location/models/location.dart';
import 'package:betracky_background_location/services/location_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:geolocator/geolocator.dart';
import 'package:globetrack/test/noti_service.dart';
import 'package:logger/logger.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:http/http.dart' as http;
class Test extends StatefulWidget {
const Test({super.key});
@override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
var logger = Logger();
LocationDataModel? _currentLocation;
late StreamSubscription _streamSubscription;
final NotiService notiService = NotiService();
Future<void> requestPermissions() async {
await Permission.location.request();
await Permission.locationAlways.request(); // For background location
await Permission.locationWhenInUse.request(); // For foreground location
}
@override
void initState() {
// TODO: implement initState
super.initState();
initializeService();
_streamSubscription = FlutterBackgroundService().on("update").listen((locationData) {
logger.e("Location Data: $locationData");
setState(() {
_currentLocation = LocationDataModel.fromJson(locationData!);
});
});
}
Future<void> uploadLocation(LocationDataModel locationData) async {
var logger = Logger();
// Upload location data to server
String baseUrl = "https://api.betracky.com";
String endpoint = "/api/v1/locations";
String url = "$baseUrl$endpoint";
String access_token = "your_access_token";
List<Map<String, dynamic>> data = [
{
'journey_id': 5,
'latitude': locationData.latitude,
'longitude': locationData.longitude,
'actual_created_time': locationData.timestamp.toIso8601String(),
}
];
try{
var response = await http.post(
Uri.parse(url),
headers: {
"Authorization": "Bearer $access_token",
'Content-Type': 'application/json',
},
body: jsonEncode(data),
);
logger.e("Response: ${response.body}");
if (response.statusCode == 200) {
print('Data uploaded successfully.');
notiService.showNotification(title: 'Location Uploaded', body: 'Your location :. ${locationData.timestamp}');
}else{
print('Failed to upload data: ${response.statusCode}');
}
}catch(e){
print('Error: $e');
}
}
Future<void> initializeService() async {
await requestPermissions();
FlutterBackgroundService().on("update").listen((locationData) {
if (locationData != null) {
var logger = Logger();
logger.e("Location Data: $locationData");
LocationDataModel location = LocationDataModel.fromJson(locationData);
// uploadLocation(location);
}
});
try{
BeTrackyBackgroundLocation.startService(
distanceFilter: 0,
accuracy: LocationAccuracy.high,
startOnBoot: true,
foregroundService: true,
offlineEnabled: false, // Optional
url: "your_endpoint", // Optional
access_token: "your_endpoint_access_token", // Optional
id: your id, // Optional
betrackyToken: "e6fac1fd-1bc0-449b-a562-22b9b916e3098jhA", // use this key
);
}catch(e){
logger.e('ErrorBetracky: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Location Tracker'),
),
body: Center(
child: _currentLocation == null
? CircularProgressIndicator()
: Column(
children: [
Text(
'📍 Location: ${_currentLocation!.latitude}, ${_currentLocation!.longitude}',
style: TextStyle(fontSize: 20),
),
ElevatedButton(onPressed: (){
BeTrackyBackgroundLocation.stopService();
}, child: Text('Stop Service'))
],
),
),
);
}
}