How to use Google Maps directions API in Flutter (is it a good practice to keep calling directions API while current location is changing on the map) - flutter

In Flutter I use
google_maps_flutter, google_directions_api and flutter_polyline_points packages to have a map with the following functionalities;
Drawing routes between current location and destination points, get distance and durations between them, driver must be notified to take left/right while driving to the destination.
I have done these all, but when current location keeps updating on the map I'm calling direction api which return lots of data like legs and steps this api call is almost every second and google will charge me a lot.
Is anybody faced to same issue, I really appreciate a help.
There is part of my codes I have done so far
void _getCurrentLocation(context) {
showGeneralDialog(
context: context,
barrierDismissible: false,
barrierColor: Colors.black45,
pageBuilder: (BuildContext buildContext, Animation animation,
Animation secondaryAnimation) {
return Center(
child: Container(
width: MediaQuery.of(context).size.width - 10,
height: MediaQuery.of(context).size.height - 80,
padding: EdgeInsets.all(20),
color: Tingsapp.transparent,
child: CurrentLocation(),
),
);
}).then((location) {
if (location != null) {
_addMoverMarker(location, 'mover');
//updateFirebase(location);
_animateCameraToCurrentLocation(location);
}
});
}
destination point are already set.
In the above code I get user current location and add a marker as bellow
void _addMoverMarker(newLocationData, String id) async {
Uint8List imageData = await getMarker();
//LatLng latlng = LatLng(newLocationData.latitude, newLocationData.longitude);
LatLng latlng = LatLng(_moverLatitude!, _moverLongitude!);
MarkerId markerId = MarkerId(id);
Marker marker = Marker(
markerId: markerId,
position: latlng,
zIndex: 2,
icon: BitmapDescriptor.fromBytes(imageData),
infoWindow: InfoWindow(
title: "Mover location",
),
);
markers[markerId] = marker;
circle = Circle(
circleId: CircleId("circle"),
radius: 20,
zIndex: 1,
center: latlng,
strokeColor: Colors.orange.withAlpha(60),
fillColor: Colors.orange.withAlpha(300),
);
_getMoverPolyline(newLocationData);
}
and here I animate the camera
_animateCameraToCurrentLocation(newLocationData) {
if (_locationSubscription != null) {
_locationSubscription!.cancel();
}
_locationSubscription =
_locationTracker.onLocationChanged.listen((newLocationData) {
if (_mapController != null) {
_addMoverMarker(newLocationData, 'mover');
_animateCamera(_moverLatitude, _moverLongitude, 16.0);
//updateFirebase(newLocationData);
}
});
}
when I draw the polyline I call directions api here my problem starts
_getMoverPolyline(LocationData locationData) async {
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
mapKey,
PointLatLng(_originLatitude!, _originLongitude!),
PointLatLng(_moverLatitude!, _moverLongitude!),
//PointLatLng(locationData.latitude!, locationData.longitude!),
travelMode: TravelMode.driving,
);
if (result.points.isNotEmpty) {
moverPolylineCoordinates = [];
result.points.forEach((PointLatLng point) {
moverPolylineCoordinates.add(LatLng(point.latitude, point.longitude));
});
}
_addMoverPolyLine();
_getDirections(_moverLatitude, _moverLongitude, _originLatitude,
_originLongitude).then((data){
_updateData(data);
});
}
_getDirections(_moverLatitude, _moverLongitude, _originLatitude, _originLongitude) async {
Api api = Api();
var res = await api.getDirections(
_moverLatitude, _moverLongitude, _originLatitude, _originLongitude);
var jsonData = jsonDecode(res.body);
print(jsonData['routes'][0]['legs'][0]);
return jsonData['routes'][0]['legs'][0];
}
In the above code _getDirections method gets calling every second.
Isn't possible to call directions api one time?
_updateData method update data like tern right/left or Head south on my map

No I don't think that it's good practice to keep calling the API. And even in the google_directions_api documentation, they say,
Note: This service is not designed to respond in real time to user input.
And to answer your main question..
Don't call the API every time the location changes, instead call it once using the current location. And the response contains everything you need to navigate the user. Check the maneuver key inside each step of a leg.
And you should only use the location subscription to update the current location and animate the camera on the map. Use the getLocation() method to get the current location before starting the trip and use that location to generate the route. Store that route in a variable and use the data inside the route response to display whatever you want.

It´s not a good practice to constantly call directionsAPI, for the good of your pocket and to follow the recommendations of google.
If you are creating a Navigation app you first need to call the Api when the user set a destination, capture de coordinates (overview_polyline) and after that if the user goes off the road you can use maps toolkit to determine when this occurs and after that call again directionsAPI.

Related

Flutter setState is not reloading page

I am building a Flutter app that allows users to create locations and then have those locations uploaded to a database and shown to other users on a map. I have gotten the database and displaying of locations to work properly, but I want the map, a Google Map, to dynamically reload after the user has created a location and is returned to the map screen.
Right now, I am using Firebase Realtime Database to listen for changes and trying to update the map accordingly after the user has clicked on the button to create a new location. Here is the code for that:
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const LocationCreation()),
).then((valuef) {
locationsRef.onValue.listen((DatabaseEvent event) {
final data = event.snapshot.value;
if (event.snapshot.value != null) {
allMarkers = {};
final map = data as Map<dynamic, dynamic>;
map.forEach((key, value){
print(value['name']);
allLocations.add(value);
// Code to create a Marker object called marker
}
);
setState(() {
allMarkers.add(marker);
});
});
}
});
I know this code is reached, because the print statement is being called and the correct names are being printed out, including the name of the newly created location. However, the map screen is not being updated with the new locations and their markers.
The Google Map is being built in the build function as so:
GoogleMap(
scrollGesturesEnabled: true,
onMapCreated: (onCreated),
initialCameraPosition: CameraPosition(
target: _center,
zoom: 11.0,
),
markers: allMarkers,
),
And here is the onCreated function:
print('here again');
mapController = controller;
final snapshot = await locationsRef.get();
final map = snapshot.value as Map<dynamic, dynamic>;
setState(() {
allMarkers.clear();
map.forEach((key, value) {
allLocations.add(value);
//Code to make a Marker called marker
allMarkers.add(marker);
});
});
The print statement is called once upon initial building of the app, but is not called again after a new location has been created and the user is returned to the map screen.
allMarkers is not being initialized within the build function, which I found was a common problem for others.
Each time I try to make a new location, the database will store the new location and the print statement within the listen for new database event will be called, but it seems that Flutter does not detect a change to allMarkers. I have also tried allMarker.clear(), but the problem is the same. I have also tried to move the call to setState(), but nothing has worked.

Change active marker appearance - flutter google maps

I'm using the flutter GoogleMap widget in my app to which I am adding markers :
Generated by mapping my bloc state.locationPoints
I've successfully implemented different appearances for those BitmapDescriptor markers through my customMarker() method
Now I'd also like to define a separate appearance for the same marker depending on wether it is active (tapped or not)
I've tried doing so using setState but it changes all of my markers appearances while I only want the current one to be changed
BlocBuilder<LocationBloc, LocationState>(
builder: (context, state) {
var markers = <Marker>{};
if (state is LocationLoaded) {
markers = state.locationPoints!.map((locationPoint) {
return Marker(
onTap: () {
},
icon: customMarker(locationPoint),
position: LatLng(
locationPoint.coordinates.latitude,
locationPoint.coordinates.longitude,
),
);
}).toSet();
}
return GoogleMap(
markers: markers,
);
},
);
You have to find the specific marker in your set of markers. If you provide your marker with an ID that contains some information from locationPoint, you could do something like this (in my case I use my place id as markerId):
final markers = state.markers
.map(
(marker) => marker.markerId.value == state.selectedPlace.id
? marker.copyWith(
iconParam: state.saveSelectedMarkerIcon,
)
: marker,
)
.toSet();

setState alternative for abstract class?

I want my map to update every time a new marker is added to it. I want to add some abstraction to my app, so I decided to dedicate the marker management to a MarkerManager class. Problem is, I now don't have the possibility to use setState, which I used for updating earlier. The markers appear when the map cursor is moved, though. Is there a way to update my screen without using setState? Here is my MarkerManager class :
class MarkerManager {
void addSimpleMarker(LatLng latlng) {
markers.add(
simpleMarker(latlng)
);
}
Marker simpleMarker(LatLng latlng) {
return Marker(
width: 40.0, height: 40.0,
point: latlng,
builder: (ctx) => FlutterLogo()
);
}
}
Thanks in advance!
You should use some state manager like Provider or Bloc

location.onLocationChanged is calling even when i am not moving at all

Following Code shows that in a flutter widget I have used location.onLocationChanged listeners so that i can record some points while a person moves and later show them on the map as a path. but it is not working as soon as i come on this same page it starts to call continuously.Even when I am not moving.
#override
void initState() {
_progressHUD = new ProgressHUD(
backgroundColor: Colors.black12,
color: Colors.red,
containerColor: Colors.white,
borderRadius: 5.0,
loading: false,
text: 'Updating...',
);
setState(() {
isEnding = false;
});
checkLocation();
getCurrentRide();
location.changeSettings(accuracy: LocationAccuracy.high);
locationSubscription =
location.onLocationChanged.listen((LocationData currentLocation) {
// Use current location
if (currentRideId != null && !isEnding) {
final collRef = Firestore.instance.collection('rideLocations');
DocumentReference docReference = collRef.document();
docReference.setData({
"lat": currentLocation.latitude,
"long": currentLocation.longitude,
"time": new DateTime.now(),
"locationId": docReference.documentID,
"rideId": currentRideId,
}).then((doc) {
Timer(Duration(milliseconds: 150), () {
print("init location timer");
// Navigator.pop(context, true);
});
}).catchError((error) {
print(error);
});
}
print(
"currentLocation is --------------- ${currentLocation.latitude}${currentLocation.longitude}");
print("ongoing ref $currentRideId");
});
super.initState();
}
You have some request options for getting locations on how accurate and often they are.
Accuracy, Interval, Distance
But you have just only tell the library accuracy. Interval and distance are passed as their default values
location.changeSettings(accuracy: LocationAccuracy.high);
It's required to tell those params to the library for your scenario. Just an example,
location.changeSettings(accuracy: LocationAccuracy.high.
interval: 10000,
distanceFilter: 5
);
It basically means,
If 10 seconds are passed AND* if the phone is moved at least 5 meters, give the location.
AND*, on Android, interval and distanceFilter are interpreted together. So, only 10 seconds or only min. 5m movement won't trigger the onLocationChanged method
Firstly, even if you are not moving, there may be some drifting so location changes to a tiny bit.
Secondly, try distinct like:
ocation.onLocationChanged
.distinct()
.listen((LocationData currentLocation) { ... });
you just put single line code
i am using this plugin
->https://pub.dev/packages/location
//interval add secode in milisecond and add distance
location.changeSettings(interval: 5000,distanceFilter: 5);

How to get marker lat and long on flutter map?

I'm using mapbox on flutter with flutter_map package and need to add marker on map when tapped on map and then get marker lat and long to send to server.
As far as I know, this functionality isn't available yet. There is however a pull request you can use to get this functionality while you wait for the Flutter team to merge it into the flutter_google_maps repo. You can find the pull request and code here
When using this pull request version you can do something like the below:
void _onMapLongTapped(LatLng location) {
// add place marker code here
_addMyMarker(location);
}
Future<void> _addMyMarker(LatLng location) async {
if (_myMarker != null) {
mapController.updateMarker(
_myMarker,
MarkerOptions(
position: LatLng(
location.latitude,
location.longitude,
)),
);
} else {
_myMarker = await mapController.addMarker(
MarkerOptions(
position: LatLng(
location.latitude,
location.longitude,
),
),
);
}}