I'm a total noob with flutter and I'm trying to create an app that shows me some markers on a map pointing the location of some sensors. The icons for the markers will be different depending on the data collected for each sensor.
So far I have managed to:
What I need:
This is how I display markers on the map:
@override
Widget build(BuildContext context) {
var userLocation = Provider.of<UserLocation>(context );
Set<Marker> markers = Set();
//this cycle creates markers with sensor's data
for(var i=0; i < sensors.length ; i++){
var tempF=(strSensors[i]["temp_f"]).toString();
double pm2_5= double.parse(strSensors[i]["PM2_5Value"]);
//It returns a tuple wit Aqi value(item1) and description(item2). Depending on item1 value the marker's icon should change.
var Aqi=AqiCalculation().AqiValue(pm2_5);
//my attempt to update the value of the asset's path depending on Aqi.item1 value
setState((){
marcador=updateCustomMapPin(Aqi.item1);
});
Marker resultMarker = Marker(
markerId: MarkerId(strSensors[i]["ID"].toString()),
position: LatLng(strSensors[i]["Lat"].toDouble(),strSensors[i]["Lon"].toDouble()),
icon: BitmapDescriptor.fromBytes(markerIcon),
onTap: (){
//shows an image in dialog box depending on Aqi value
if(Aqi.item1 > 80){
image="https://i.gifer.com/72fB.gif";
}
else{
image= "https://raw.githubusercontent.com/Shashank02051997/FancyGifDialog-Android/master/GIF's/gif14.gif";
}
String tempC;
tempC=((int.parse(tempF)-32)/1.8).toStringAsPrecision(4);
showDialog<void>(
context: context,
builder: (_) => NetworkGiffyDialog(
key: keys[1],
image: Image.network(
image,
fit: BoxFit.cover,
// alignment:Alignment.bottomLeft,
),
entryAnimation: EntryAnimation.TOP_LEFT,
title: Text('Sensor: ${strSensors[i]["Label"]}'
,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0, fontWeight: FontWeight.bold),
),
description: Text(
'PM2.5: $pm2_5 Temp:${tempC}° Aqi:${Aqi.item1} Status:${Aqi.item2}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.0, fontWeight: FontWeight.normal),
),
onlyOkButton: true,
onOkButtonPressed: (){
Navigator.pop(context);//closes dialogbox
},
)
);
}
);
// Add it to Set
markers.add(resultMarker);
}
To change the default marker icon I did this:
@override
void initState(){
super.initState();
setCustomMappin('android/assets/Cloudgray.png');
}
//Converts image to Unint8List
Future<Uint8List> getBytesFromAsset(String path, int width) async {
ByteData data = await rootBundle.load(path);
ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width);
ui.FrameInfo fi = await codec.getNextFrame();
return (await fi.image.toByteData(format: ui.ImageByteFormat.png)).buffer.asUint8List();
}
void setCustomMappin(marker)async {
markerIcon = await getBytesFromAsset(marker, 150);
}
This changes all the icons to my default icon's asset. I tried to update the asset's path dynamically by passing this on the loop that creates all the markers:
setState((){
marcador=updateCustomMapPin(Aqi.item1);
});
which uses this function:
//returns icon path depending on the value
updateCustomMapPin(value) {
String marker='';
if(value<=50){
marker = 'android/assets/Cloudgreen.png';
}
else if(value>50 && value<=100){
marker='android/assets/Cloudyellow.png';
}
else if(value>100 && value<=150){
marker='android/assets/CloudOrange.png';
}
else if(value>150 && value<=200){
marker='android/assets/Cloudred.png';
}
else if(value>200 && value<=300){
marker='android/assets/Cloudpurple.png';
}
else{
marker='android/assets/Cloudtoxic.png';
}
return marker;
}
And this is where I'm stuck, the marker's icon doesn't change despite that, at the moment when every marker is created the proper icon is indicated.
Maybe is a problem of me understanding the life cycle of a Flutter app, I hope you can help me.
I solve this by setting all the custom pins in the initState method, the problem occurred beacause the convertion of the images to bytes is an async process and due to the way I set the dynamic election of the markers it didn't have enough time to make that convertion. So what I did was set a method that converted every image to bytes in the initState, pass those values to global variables and then use them in a conditional for every case.