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();
Related
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.
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.
Objective:
To be able to show custom markers from dev only and disable google maps' default markers.
Description:
I am trying to put markers in GoogleMap from google_maps_flutter plugin but Google already has its own markers so it is getting in the way of the markers that I am trying to add. Is there any way to just show the map and add user-defined markers only? If not is it possible to minimize the number of markers shown by default map?
Just looked around and found some possible fix.
Seems like we can generate map style from this website:
Styling Wizard.
From there I toned down landmarks and then I was able to remove markers using this:
final String mapStyle =
await rootBundle.loadString('assets/map/map_style.txt');
//Set it on mapcontroller after map is created.
onMapCreated: (GoogleMapController controller) {
if (_controller.isCompleted) {
return;
}
controller.setMapStyle(mapStyle);
_controller.complete(controller);
},
// create a function to create custom marker
Future<BitmapDescriptor> createCustomMarkerBitmap() async {
Uint8List? data = await getBytesFromAsset("assets/icons/map_marker.png", 100);
return BitmapDescriptor.fromBytes(data!);
}
// then call the function to create a custom marker.
BitmapDescriptor? _marker = await createCustomMarkerBitmap();
final Marker marker = Marker(
markerId: _markerId,
position: _position,
icon: _marker, // use the marker
infoWindow: _infoWindow,
onTap: () {},
);
I think my issue is the same as here:
Flutter show marker's infoWindowText by default in Google map widget
However, there is no answer, except for a link to another post which isn't quite what I want to do.
When my map opens in my app, it shows the markers correctly, but I also want to have all the labels displaying by default on open...
As the shown by one of the markers here:
I'm creating markers like this:
for (Location location in _locations) {
final locationMarker = Marker(
markerId: MarkerId(location.id),
position: LatLng(location.geoY, location.geoX),
infoWindow: InfoWindow(
title: location.name,
),
onTap: () => _onTap(location),
icon: _mapMarkers.icon(location.slug),
draggable: false);
markers.add(locationMarker);
}
Then the map like this:
Widget _map(Set<Marker> markers) => GoogleMap(
mapType: MapType.normal,
initialCameraPosition: CameraPosition(target: widget.mapCentre),
markers: markers,
...
onMapCreated: (controller) => _onMapCreated(controller, markers),
);
And the in _onMapCreated, I've tried to show all the labels, but only the last in the list of markers is actually displayed:
_onMapCreated(GoogleMapController controller, Set<Marker> markers) {
if (mounted) {
setState(() {
_controller.complete(controller);
controller.setMapStyle(_mapStyle);
for (var marker in markers) {
controller.showMarkerInfoWindow(marker.markerId);
}
});
...
I'm using:
[{
"featureType": "poi",
"stylers": [
{
"visibility": "off"
}
]
}]
to remove points of interest from the maps and I am hoping I can do something similar to get marker labels to display by default, but so far I've not found anything when searching google, or that there's another solution.
You can use showMarkerInfoWindow property the GoogleMapController
final GoogleMapController controller = await _controller.future;
controller.showMarkerInfoWindow(MarkerId('ID_MARKET'));
I am Vaibhav Pathak. I am working on a Flutter app in which I add markers in-app based on data change in firestore database and I want to remove the previous marker from the map using its marker id but I don't understand how to do it. I had watched many Youtube videos and blogs but I don't get to know this because of API changes in google maps flutter plugin. For your kind information, I want to tell that I am using the latest version of the google_maps flutter plugin.
My Code for Making Markers :
showLiveLocation(LatLng latLng) {
_markers.add(
Marker(
markerId: MarkerId(latLng.toString()),
position: latLng,
draggable: false,
infoWindow: InfoWindow(
title: "Live Order Location",
snippet: "Dear customer your order is live at this place."),
icon: liveLocation,
visible: true,
),
);
}
Thanks for everyone's help.
github : github#vkpdeveloper
You need to find that specific marker in your _markers list (e.g. by firstWhere()) and then remove it from the list of markers.
Edit:
Marker marker = _markers.firstWhere((marker) => marker.markerId.value == "myId",orElse: () => null);
setState(() {
_markers.remove(marker);
});
This will trigger a rebuild of your map where the marker is no longer included.
Inspired by Thomas, my solution was:
setState(() {
_markers.removeWhere((key, marker) => marker.markerId.value == "myMarkerId");
});