I have a Flutter application that allows an administrator to create a route on a map by saving the latitude and longitude. The admin can then save this route so that users can use it to find the admin-set fishing points of interest. However, I encountered an issue where the application stopped tracking the route when the admin put their phone in their pocket or locked the screen. I’ve been told that I can solve this problem by using background services.
Here is my code for tracking:
class TrackRouteScreen extends StatefulWidget {
final String id;
TrackRouteScreen({required this.id});
@override
State<TrackRouteScreen> createState() => _TrackRouteScreenState();
}
class _TrackRouteScreenState extends State<TrackRouteScreen> {
bool isTracking = false;
final CollectionReference routesCollection = FirebaseFirestore.instance.collection('Fish POI');
late MapController mapController;
LatLng? currentPosition;
List<LatLng> polylineCoordinates = [];
List<Marker> markers = [];
StreamSubscription<Position>? positionStreamSubscription;
@override
void initState() {
initialize();
super.initState();
}
Future<void> initialize() async {
mapController = MapController();
currentPosition = await getCurrentLocation();
setState(() {});
}
// Get current location of the user
Future<LatLng> getCurrentLocation() async {
bool serviceEnabled;
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
throw Exception('Location services are disabled');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission==LocationPermission.denied||permission == LocationPermission.deniedForever) {
throw Exception('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
throw Exception('Location permissions are permanently denied');
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high
);
return LatLng(position.latitude, position.longitude);
}
// Start tracking
void startTracking() {
setState(() {
isTracking = true;
polylineCoordinates.clear();
});
positionStreamSubscription = Geolocator.getPositionStream(
locationSettings: LocationSettings()).listen((Position position) {
updateLocation(position);
}
);
}
// Update location and polyline
void updateLocation(Position position) {
setState(() {
currentPosition = LatLng(position.latitude, position.longitude);
polylineCoordinates.add(currentPosition!);
});
}
// Stop tracking
void stopTracking() {
setState(() {
isTracking = false;
});
positionStreamSubscription!.cancel();
saveTrackedRoute(polylineCoordinates);
}
// Save tracked route to Firestore DB
Future<void> saveTrackedRoute(List<LatLng> trackedRoute) async {
try {
final DocumentReference routeDocRef = routesCollection.doc(widget.id);
await routeDocRef.update({
'route': trackedRoute .map((latLng) => GeoPoint(latLng.latitude, latLng.longitude))
.toList(),
});
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AddImagesScreen(id: widget.id)),
);
} catch (error) {
print('Error saving tracked route: $error');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(body: body());
}
Widget body() {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
FlutterMap(
mapController: mapController,
options: MapOptions(center: currentPosition, zoom: 15),
children: [
TileLayer(
urlTemplate: mapboxUrlTemplate,
additionalOptions: {
'accessToken': mapboxAccessToken,
'id': 'mapbox.mapbox-streets-v8',
},
),
PolylineLayer(
polylines: [
Polyline(
points: polylineCoordinates,
strokeWidth: 5,
color: Colors.blue,
),
],
),
CurrentLocationLayer(
followOnLocationUpdate: FollowOnLocationUpdate.always,
style: LocationMarkerStyle(
marker: DefaultLocationMarker(),
),
),
],
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 20),
child: Align(
alignment: Alignment.bottomCenter,
child: GradientElevatedButton(
onTap: isTracking ? stopTracking : startTracking,
width: MediaQuery.of(context).size.width,
beginningColor: isTracking ? Colors.red : Colors.green,
endingColor: isTracking ? Colors.red : Colors.green,
text: isTracking ? 'Stop' : 'Start',
),
),
),
Positioned(
top: 40.0,
right: 15.0,
child: FloatingActionButton(
onPressed: () {
if (isTracking) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Quit Tracking'),
content: Text('Are you sure you want to quit tracking?'),
actions: <Widget>[
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: Text('Quit'),
onPressed: () {
Navigator.of(context).pop();
Navigator.of(context).pop();
stopTracking();
},
),
],
);
},
);
} else {
Navigator.pop(context);
}
},
child: Icon(Icons.close),
backgroundColor: Colors.grey[300],
foregroundColor: Colors.black,
),
),
],
),
);
}
}
As a 17-year-old girl who is new to app development, I find this concept confusing. What does it mean to track in the background? How can I implement it? Additionally, I’m worried about users who may not have a data connection. In such cases, where can the data be stored until a connection is available again?
Can someone give me a solid understanding of this, modify the code and explain it in a simple way? And which package will suit this?
2
Answers
If i, understood your purpose correctly, you can try this package https://pub.dev/packages/flutter_background_service
It’s impressive that you’re pursuing app development at such a young age! I’ll do my best to explain background tracking and assist you in simplifying the code. Not that I am a professional in this area, but it seems like people are ignoring you.
Background tracking refers to your application’s ability to continue tracking the user’s location even when the app is not actively operating or the screen is locked. This is essential for situations where the administrator wants to track routes without keeping the app open all the time, such as with your fishing app.
To implement background monitoring, use the background_location_tracker Flutter package. This package enables you to monitor the location of the user in the background and execute code when location updates are received.
To implement location monitoring in the background with background_location_tracker:
Also, compileSdkVersion and targetSdkVersion should be at least 29. Modify android/app/build.gradle as follows:
In your main.dart file, you should initialize the package. This is how your main.dart file could look like:
After initializing the package, let’s modify your TrackRouteScreen. First, import the necessary packages:
Update your _TrackRouteScreenState class with the required modifications for background location tracking. Add necessary member variables:
Add the functions you need:
Make sure to call the startTracking function when the ‘Start’ button is pressed, and make sure to call the stopTracking function when the ‘Stop’ button is pressed.
Your app will now monitor the user’s location even when the screen is locked or the app is in the background.
Concerning your concern regarding users without a data connection, you can store tracked data locally using a database or file until a data connection becomes available. Use a package such as sqflite to store the data in a local SQLite database or shared_preferences to store it as key-value pairs on the device. When an available data connection is present, you can then submit the data to your Firestore database. But this is it’s own topic, which you can ask in another question.
I hope this explanation and the code modifications make it simpler for you to comprehend and implement background tracking in your app.