How to change the positions of markers on the google map every 10 seconds in Flutter? - google-maps

Inside the 10 seconds Timer function which calls the API CONTINOUSLY, the markers (positions - latitudes and longitudes) on the google map are loaded properly at the FIRST TIME. But, the problem is that the updated markers are NOT EFFECTING at the SECOND TIME in the Widget.
FIRST TIME refers to the Timer function which executes the codes after 10 seconds at the first time.
SECOND TIME refers to the Timer function which executes the codes after 10 seconds at the second time.
class _MyAppState extends State<Monitor> {
// Initialize the markers
late Set<Marker> _markers = new Set();
// Initialize the Google Map
late Completer<GoogleMapController> _controller = Completer();
// Initialize the camera possition
late CameraPosition _currentPosition;
// Initialize the time to be displayed
late String? time = "";
// At the very beginning of the app
#override
void initState() {
super.initState();
// Set the camera possition of the google map
_currentPosition = CameraPosition(
target: LatLng(7.8731, 80.7718),
zoom: 7.5,
);
// Start initializing the variables for GIS information
dynamic lat = "";
dynamic lon = "";
dynamic id = "";
dynamic vehicle = "";
dynamic mtime = "";
dynamic speed = "";
// End initializing the variables for GIS information
// Start the Timer which executes the codes for each 10 seconds **************************************************************************
Timer mytimer = Timer.periodic(Duration(seconds: 10), (timer) async {
//get current date and time
DateTime timenow = DateTime.now();
time = timenow.hour.toString() + ":" + timenow.minute.toString() + ":" + timenow.second.toString();
// Call the API which sends back the GIS Information
MonitorProcess monitorProcess = new MonitorProcess();
http.Response response = await monitorProcess.getMapDetailsForMonitorProcess();
// Get the GIS information
dynamic responseBody = jsonDecode(response.body);
// Set state to change the states and behavoirs
setState(() {
// Cear old markers before new markers comming
_markers.clear();
// Start storing makers for google map -------------------------------------------------
for (var i = 0; i < responseBody['response'].length; i++) {
lat = responseBody['response'][i]['lat'];
lon = responseBody['response'][i]['lon'];
id = responseBody['response'][i]['id'].toString();
vehicle = responseBody['response'][i]['vehicle'];
mtime = responseBody['response'][i]['mtime'];
speed = responseBody['response'][i]['speed'].toString();
_markers.add(Marker(
markerId: MarkerId(id.toString()),
position: LatLng(double.parse(lat), double.parse(lon)),
infoWindow: InfoWindow(
title: vehicle,
snippet: lat +
"," +
lon +
"Time: " +
mtime +
"Speed: " +
speed.toString() +
"vehicle: " +
vehicle,
)));
}
// End storing makers for google map ---------------------------------------------------
});
//mytimer.cancel() //to terminate this Timer
});
// End the Timer which executes the codes for each 10 seconds ******************88********************************************************
}
// Start displaying the google map markers in the widget ---------------------------------------
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Test GIS 001",
home: Scaffold(
appBar: AppBar(
title:Text("Display GIS Information Every 10 Seconds"),
backgroundColor: Colors.redAccent,
),
body: Container(
//height: 260,
color: Colors.red.shade50,
child: /*Center(
child: Text(time, style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),),
//show time on UI
)*/
GoogleMap(
initialCameraPosition: _currentPosition,
markers: _markers,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
)
)
);
}
// End displaying the google map markers in the widget ---------------------------------------
}
There is nothing wrong with the API because the updated latitudes and longitudes and other details are coming for each 10 seconds continuously.
I tried to change the state of the widget using setState() method, BUT HERE THE PROBLEM is that the widget is not updating the contents which holds google map markers.

Related

Flutter - Provide Real-Time Updates to Google Maps Using StreamBuilder

I'm currently building an application that displays markers on a Google Map. The location of the markers is being pulled from a Firestore database. Currently, I am successfully retrieving and displaying all markers. However, I need to update the icon of each marker based on their boolean value in the database.
Here is how I am currently displaying the markers.
I create the map in the main build:
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: mapToggle
? GoogleMap(
onMapCreated: onMapCreated,
compassEnabled: false,
polylines: route,
initialCameraPosition: CameraPosition(
target: LatLng(currentLocation.latitude,
currentLocation.longitude),
zoom: 12),
markers: markers,
)
: Center(
child: Text('Loading... Please wait...',
style: TextStyle(fontSize: 20.0)),
)),
Then I call this function in the initState to grab my markers:
chargePoints() {
_database.collection('charge_points').getDocuments().then((docs) {
if (docs.documents.isNotEmpty) {
for (int i = 0; i < docs.documents.length; i++) {
initMarker(docs.documents[i].data, docs.documents[i].documentID);
}
}
});
Finally, the initMarker function adds each marker to the Set marker, taken by the Google Map instance:
void initMarker(chargePoint, documentID) {
var markerIdVal = documentID;
final MarkerId markerId = MarkerId(markerIdVal);
// Creating a new Charge Point Marker
final Marker marker = Marker(
markerId: markerId,
icon: markerIcon,
position: LatLng(chargePoint['location'].latitude,
chargePoint['location'].longitude),
infoWindow: InfoWindow(
title: chargePoint['name'], snippet: chargePoint['type']),
onTap: () {
findBookings(context, chargePoint, documentID);
});
setState(() {
markers.add(marker);
});
So my question is; how can I return this data as a Stream, and provide a listener that updates the respective marker icon, when the "occupied" field has been changed?
Firestore
Map UI
Okay, I got a solution!
I'm now returning a StreamBuilder in the main build Widget that takes a Firestore query to return the details of all markers.
I then use the snapshot provided by the stream to intermittently transform the state of all markers set in the initMarker function above.
Additionally, the markers Set is now a Map. This makes more sense for accessing the markers to perform updates.
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
The markers are managed using their MarkerId, which correlates to the documentID of all results returned in the snapshot.
I don't believe this solution is perfect, but I hope I've helped someone out!
StreamBuilder<QuerySnapshot>(
stream: _servicesObj.getChargePoints(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Text('Loading Points');
}
snapshot.data.documents.forEach((change) {
var markerIdVal = change.documentID;
final MarkerId markerId = MarkerId(markerIdVal);
if (change.data['occupied'] == true) {
markers[markerId] = Marker(
markerId: markerId,
icon: pointOccupiedIcon,
position: LatLng(change.data['location'].latitude,
change.data['location'].longitude),
infoWindow: InfoWindow(
title: change.data['name'],
snippet: change.data['type']),
onTap: () {
findBookings(context, change.data, change.documentID);
});
} else {
markers[markerId] = Marker(
markerId: markerId,
icon: pointAvailableIcon,
position: LatLng(change.data['location'].latitude,
change.data['location'].longitude),
infoWindow: InfoWindow(
title: change.data['name'],
snippet: change.data['type']),
onTap: () {
findBookings(context, change.data, change.documentID);
});
}
});

Flutter Maps: How to change Marker Icon on marker tap

I'm trying to find a solution for changing the marker icon when the marker is tapped, in order to give visual feedback. Still, I haven't figured out how to do this. The first thought was accessing the marker by index but since markers are arranged in a Set<Markers> there is no change to access it in a proper way. then it would be easy to just exchange the old marker by the new one. Is there a common way to do this?
Edit:
Using Java, there are options like. setIcon for markers. This is not the chase for Flutter.
like so:
marker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.ic_selected_user_mark_icon));
Happy coding :)
I had the same problem. Google Maps API doesn't have this option. I resolved it manually.
You can create a flag and change the icon Marker in the Marker's onTap and GoogleMap's onTap properties.
int markerFlag = null;
List<Marker> _allMarkers = [];
Marker's onTap:
onTap: () {
if (markerFlag != idMarker) {
setState(() {
if (markerFlag != null) {
_allMarkers[markerFlag] = _allMarkers[markerFlag].copyWith(iconParam: defaultMarkerIcon);
}
_allMarkers[idMarker] = _allMarkers[idMarker].copyWith(iconParam: selectedMarkerIcon);
markerFlag = idMarker;
});
}
}
GoogleMap's onTap:
onTap: (value){
if (markerFlag != null) {
setState(() {
_allMarkers[markerFlag] = _allMarkers[markerFlag].copyWith(iconParam: defaultMarkerIcon);
markerFlag = null;
});
}
}
//onTap Marker:
widget.markersStation.add(
Marker(
markerId: MarkerId(markerId),
position: latLng,
icon: iconMarkerStationGrey,
onTap: () {
setState(() {
for (var j = 0; j < widget.markersStation.length; j++) {
widget.markersStation[j] = widget.markersStation[j]
.copyWith(iconParam: iconMarkerStationGrey);
}
if (idMarkerSelected != int.parse(markerId)) {
widget.markersStation[int.parse(markerId)] = widget
.markersStation[int.parse(markerId)]
.copyWith(iconParam: iconMarkerStationSelected);
idMarkerSelected = int.parse(markerId);
}
});
),)
//onTap Map :
markers: widget.markersStation.toSet(),
onTap: (position) async {
_customInfoWindowController.hideInfoWindow!();
BitmapDescriptor iconMarkerStationGrey =
await BitmapDescriptor.fromAssetImage(
const ImageConfiguration(size: Size(24, 24)),
"icons/marker_station_gris_17677#2x.png");
if (idMarkerSelected > -1) {
setState(() {
widget.markersStation[idMarkerSelected] = widget
.markersStation[idMarkerSelected]
.copyWith(iconParam: iconMarkerStationGrey);
idMarkerSelected = -1;
});
}
},

How to determine which marker was pressed on the map

I am unable to determine which marker was tapped by the user on Google maps. I've added onTap: _onMarkerTapped(MarkerId markerId) event where I try to open another page with a more detailed Google Map based on which marker user has tapped. When I add bunch of markers (as a Set of markers) to my Google map, I do use unique markerId and that's what I pass in as an argument to a private custom method _onMarkerTapped(). The issue is, inside thsi event I always ended up getting the last markerId i.e. the last marker which I added to the Set. Is there any way to get the current markerid which user has tapped?
add onTap event handler for every marker I added in a set of Markers
for (var campusData in Campuses.campusLocations) {
campusDetails = json.decode(campusData);
campus = Campus.fromJson(campusDetails);
markerId = MarkerId(i.toString());
mrkr = new Marker(
markerId: markerId, // a string for marker unique id
icon: BitmapDescriptor.fromAsset(
'assets/wsu#2x.png'), // options for hues and custom imgs
position:
LatLng(campus.latitude, campus.longitude), // lat and long doubles
onTap: () {
_onMarkerTapped(markerId);
});
campusMarkers.add(mrkr);
i++;
}
return GoogleMap(
initialCameraPosition: cameraPosition,
onMapCreated: _onMapCreated,
mapType: MapType.normal,
markers: campusMarkers,
onTap: (lng) => _onMapTapped(lng),
);
void _onMarkerTapped(MarkerId markerId) {
final Marker tappedMarker = campusMarkers.elementAt(int.parse(markerId.value));
if (tappedMarker != null) {
}
else{
print("Tapped marker is NULL..");
}
}
Looking for ways to determine which marker was tapped by the user
Pay attention to these two line:
markerId = MarkerId(i.toString());
...
onTap: () {
_onMarkerTapped(markerId);
}
You are not creating a uniqueMarkerId for each loop, you are just reassigning it, so the last one you assigned will always be used by _onMarkerTapped(markerId). To fix this, use:
final markerId = MarkerId(i.toString());
For anyone who come across this issue, please follow the code example at https://github.com/flutter/plugins/blob/master/packages/google_maps_flutter/example/lib/place_marker.dart .. especially the _add() method and _onMarkerTapped() methods. PLEASE make sure to use a Map to declare your set of markers e.g. Map<MarkerId, Marker> markers = <MarkerId, Marker>{};

How to customize myLocationEnabled button on Google Maps?

I need to create a button which is going to show me the current location of the user. That's why I use Google Maps, which have the options such this one. But, I need to customize the MyLocation Button but don't know how to do it. Could you please help me with this?
I am pretty new in Flutter though :D
I couldn't find easy way of doing it, but since everything is widget in flutter, you can put your google maps in your stack, and add iconbutton or whatever custom button you need to your stack.
GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition:
CameraPosition(target: LatLng(0.0, 0.0)),
markers: markers,
),
IconButton(
icon: Icon(Icons.battery_charging_full),
onPressed: () async {
final center = await getUserLocation();
getNearbyPlaces(center);
Marker myPosition = Marker(
markerId: MarkerId('myLocation'),
position: center == null
? LatLng(0, 0)
: LatLng(center.latitude, center.longitude),
icon: BitmapDescriptor.fromAsset(
'assets/logo.png'));
setState(() {
markers.add(myPosition);
});
},
),
],
)
So what I'm doing here is basicly,
I have Stack which helps me put a IconButton over GoogleMap. When user presssed on that button, I add a new Marker to show current position, There is a Set of Markers on my State, called markers, I'm creating a new Marker by getting the current location of the user, and adding a custom icon to that marker, and then add it to my markers(Set), to display it on GoogleMap.
my getUserLocation function:
Future<LatLng> getUserLocation() async {
LocationManager.LocationData currentLocation;
final location = LocationManager.Location();
try {
currentLocation = await location.getLocation();
final lat = currentLocation.latitude;
final lng = currentLocation.longitude;
final center = LatLng(lat, lng);
return center;
} on Exception {
currentLocation = null;
return null;
}
}
I have location: ^2.1.0 package and using it as LocationManager import 'package:location/location.dart' as LocationManager;

Flutter: How to add marker to Google Maps with new Marker API?

mapController.addMarker(
MarkerOptions(
position: LatLng(37.4219999, -122.0862462),
),
);
I've seen this code snippet in a blog post, and I'm trying to add markers to Google Maps.
The method 'addMarker' isn't defined for the class 'GoogleMapController'.
I think the library has changed and I want to know what's the new way doing this, I've looked up in the controller.dart and api reference but couldn't figure it out.
I would be happy to see some tutorials and blog posts about it, don't hesitate to share.
Yes, The google maps API has changed and the Marker API is widget based and not based in controller anymore.
By CHANGELOG.md
"Breaking change. Changed the Marker API to be widget based, it was controller based. Also changed the example app to account for the same."
I copy some pieces of code from github app example that I think is important to you
Map<MarkerId, Marker> markers = <MarkerId, Marker>{}; // CLASS MEMBER, MAP OF MARKS
void _add() {
var markerIdVal = MyWayToGenerateId();
final MarkerId markerId = MarkerId(markerIdVal);
// creating a new MARKER
final Marker marker = Marker(
markerId: markerId,
position: LatLng(
center.latitude + sin(_markerIdCounter * pi / 6.0) / 20.0,
center.longitude + cos(_markerIdCounter * pi / 6.0) / 20.0,
),
infoWindow: InfoWindow(title: markerIdVal, snippet: '*'),
onTap: () {
_onMarkerTapped(markerId);
},
);
setState(() {
// adding a new marker to map
markers[markerId] = marker;
});
}
GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: const CameraPosition(
target: LatLng(-33.852, 151.211),
zoom: 11.0,
),
// TODO(iskakaushik): Remove this when collection literals makes it to stable.
// https://github.com/flutter/flutter/issues/28312
// ignore: prefer_collection_literals
markers: Set<Marker>.of(markers.values), // YOUR MARKS IN MAP
)
I advise you take a look in example app here. There is updated to new API.
I did below example from google codelabs.
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
LatLng _center = LatLng(9.669111, 80.014007);
onMap created add single static marker
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
final marker = Marker(
markerId: MarkerId('place_name'),
position: LatLng(9.669111, 80.014007),
// icon: BitmapDescriptor.,
infoWindow: InfoWindow(
title: 'title',
snippet: 'address',
),
);
setState(() {
markers[MarkerId('place_name')] = marker;
});
}
google map widget:
GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 14.0,
),
markers: markers.values.toSet(),
),