i am new to flutter and i need to save a value inside my user document.
i can print the values but i do not know how to assign the values of LatLan to a variable and get them saved in firestore.
here is my searchMapPlaceWidget that returns them values
SearchMapPlaceWidget(
darkMode: true,
placeType: PlaceType.establishment,
language: 'se',
apiKey: kGoogleApiKey,
onSelected: (place) async {
final geolocation =
await place.geolocation;
final chosenPlace = Marker(
markerId: MarkerId(
'chosen-location'),
icon: BitmapDescriptor
.defaultMarker,
position:
geolocation.coordinates);
GoogleMapController controller =
await _mapController.future;
setState(() {
_markers.add(chosenPlace);
});
print(chosenPlace.position);
controller.animateCamera(
CameraUpdate.newLatLng(
geolocation.coordinates));
controller.animateCamera(
CameraUpdate.newLatLngBounds(
geolocation.bounds, 50));
},
),
here is the function that i wrote which is not working 😅
static void addGeoPoint(User user)async{
var pos = await location.getLocation();
GeoFirePoint point = geo.point(latitude: pos.latitude, longitude: pos.longitude);
return usersRef.document(user.id).updateData({
'myAddress' : point.data
});
}
how do i save the position values of 'chosenPlace' ?
thank you all
solved it.
GeoFirePoint myLocation = geo.point(latitude: _location.latitude, longitude: _location.longitude);
usersRef.document(widget.user.id).updateData({
'position' : myLocation.data
Related
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:
show the map on users location
Place markers for all the sensors
Change marker icon to a custom icon
Add dialog box, containing
sensor's data, for each marker
What I need:
Use different icons for markers depending on sensor's data
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'm sorry, I can't use comment.
What happen if you add log in setState ?
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.
I managed to load multiple models into the same viewer and now I am trying to extract properties and values of the elements of each model; however, when I use getPropertyDb() and executeUserFunction(), I get back only the properties of the initial model.
I started with the code from this repo and used this article to understand how to load multiple models.
First model is loaded after a redirect from the server.
function onDocumentLoadSuccess(doc) {
const geometries = doc.getRoot().search({ type: 'geometry' });
if (geometries.length === 0) {
console.error('Document contains no viewables.');
return;
}
const initViewable = geometries[0];
const svfUrl = doc.getViewablePath(initViewable);
const mat = new THREE.Matrix4();
const modelOptions = {
placementTransform: mat,
globalOffset: { x: 0, y: 0, z: 0 },
sharedPropertyDbPath: doc.getPropertyDbPath()
};
const viewerDiv = document.getElementById('MyViewerDiv');
const config = {
extensions: myExtensions
};
viewer = new Autodesk.Viewing.Private.GuiViewer3D(viewerDiv, config);
viewer.start(svfUrl, modelOptions, onLoadModelSuccess, onLoadModelError);
}
After the geometry of each model is loaded an extension does some stuff.
function MyExtension(viewer, options) {
Autodesk.Viewing.Extension.call(this, viewer, options);
}
MyExtension.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
MyExtension.prototype.constructor = MyExtension;
MyExtension.prototype.onGeometryLoadEvent = function(event) {
const myPromise = this.viewer.model
.getPropertyDb()
.executeUserFunction(userFunction);
myPromise
.then(function(retValue) {
if (!retValue) {
console.log('Model doesn\'t contain valid elemens.');
}
// do stuff...
})
.catch(err => console.log(err));
};
MyExtension.prototype.load = function() {
this.onGeometryLoadBinded = this.onGeometryLoadEvent.bind(this);
this.viewer.addEventListener(
Autodesk.Viewing.GEOMETRY_LOADED_EVENT,
this.onGeometryLoadBinded
);
return true;
};
MyExtension.prototype.unload = function() {
this.viewer.removeEventListener(
Autodesk.Viewing.GEOMETRY_LOADED_EVENT,
this.onGeometryLoadBinded
);
this.onGeometryLoadBinded = null;
return true;
};
Autodesk.Viewing.theExtensionManager.registerExtension(
'MyExtension',
MyExtension
);
function userFunction(pdb) {
// get properties of the elements
}
New models are loaded in the same viewer using an extension as well.
MyOtherExtension.prototype.onDocumentLoadSuccess = function(doc) {
// get the svfUrl of the initial geometry and set the loading options
this.viewer.loadModel(
svfUrl,
loaderOptions,
this.onLoadModelSuccessBinded,
this.onLoadModelErrorBinded
);
};
How do I update the Property Database in order to get the properties and values for all the models that are currently loaded into the viewer?
Try access a specific database through the model object:
viewer.impl.modelQueue().getModels()[index].getPropertyDb()
i'm trying to do a Dashboard with multiples jsons requests, but i want that request work one per one, like when finish first request start the second, when finish second start the third when finish third start the N.
my list code:
new CustomScrollView(
cacheExtent: height * 6,
slivers: [
new SliverList(
delegate: new SliverChildListDelegate(
[
new RelatorioVendPeriodoAPeriodo(),
new RelatorioMensals(),
new RelatorioDiasDaSemanas(),
new RelatorioVendasTotalidasPorPeriodo(),
new RelatorioDasVendasTotsProdutos(),
]
)
)
]
),
this new classes calls, returns for me request. Anyone knows how to delay it?
First, the parent widget should return a progress bar when the necessary data is not available.
A service will be called an initState to fetch data from the backend. when data is ready setState() will be called to redraw the widget.
Look at this example:
class _TestWidgetState extends State<TestWidget> {
var data;
#override
void initState() {
data = NetworkService.getData().then((data) {
setState(() {
this.data = data;
});
});
}
#override
Widget build(BuildContext context) {
if (data == null) {
return CircularProgressIndicator();
} else {
return
new CustomScrollView(
cacheExtent: height * 6,
slivers: [
new SliverList(
delegate: new SliverChildListDelegate(
[
new RelatorioVendPeriodoAPeriodo(data: data),
new RelatorioMensals(data: data),
new RelatorioDiasDaSemanas(data: data),
new RelatorioVendasTotalidasPorPeriodo(data: data),
new RelatorioDasVendasTotsProdutos(data: data),
]
)
)
]
);
}
}
}
class NetworkService {
final JsonDecoder _decoder = new JsonDecoder();
static String data1;
static String data2;
static getData() async {
if (data1 == null || data2 == null) {
await fetchFromServer();
}
return {'data1': data1, 'data2': data2};
}
static fetchFromServer() async {
data1 = (await http.get('url')).body;
data2 = (await http.get('url')).body;
}
}
Future.delayed(const Duration(milliseconds: 500), () {
print(" This line is executed after 5 seconds");
});
Hello I am attempting to change an array of JSON objects to a TypeScript class. However the method seems to crash every I attempt to assign a Json object attribute to a typescript attribute.
Typescript interface
export interface marker {
lat: number;
lng: number;
}
Typescript method
public markers: marker[];
ngOnInit() {
this.mapService.GetPhotosById(this.id).subscribe(resultlisting => {
this.values = resultlisting;
console.log(this.values); //SHOW IN PICTURE
this.ChangetoMarkers(this.values);
}, error => this.errorMessage = error);
}
ChangetoMarkers(someArray: Observable<any[]>) {
for (let entry of someArray) {
let mark: marker;
mark.lat = Number(entry.latitude); //Attempt to convert to number
mark.lng = +entry.longitude; //2nd attempt to convert to number
this.markers.push(mark);
};
console.log(this.markers); //DOES NOT REACH THIS
}
Map Service
GetPhotosById(id: string): Observable<any> {
return this.http
.post(this.config.apiEndpoint + "mapPhotos/" + id)
.map(this.extractJson).catch(this.handleErrors);
};
private extractJson(res: Response) {
let data = res.json();
return data;
}
private handleErrors(error: Response | any) {
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.log(errMsg);
return Observable.throw(errMsg);
}
I have researched the issue and have attempted to apply the interger cast but it does not seem to work. Any suggestions would be great.
As Mauricio Poppe noted in his comment, you are trying to access the properties of a variable that has no value.
changeToMarkers(points: Array<{latitude: number, longitude: number}>) {
this.markers = entries.map(({latitude: lat, longitude: lng}) => ({
lat,
lng
}));
console.log(this.markers);
}
No number conversions are necessary because the deserialized representations of latitude and longitude are already compatible numeric values.
Try this:
ChangetoMarkers(someArray: any[]) {
console.log("array",someArray);
for (let entry of someArray) {
let mark: marker = {lat: entry.latitude, lng: entry.longitude};
console.log("mark", mark);
this.markers.push(mark);
};
console.log("markers",this.markers);
}
It isn't as elegant as Aluan Haddad's, but it should allow you to determine at which point it is breaking if for some reason this still doesn't work, by logging someArray before the loop, the marks before being pushed, and this.markers after to determine exactly where the problem is breaking down.
I have a Google maps component in a React/Redux app. When you click an item from a list, it passes down an array of coordinates to render as directions from the user's current location. The props are being passed fine through react-redux mapStateToProps. I'm calling a function to generate the the polyline, this is where my problem is. The marker is generated fine inside of render, but the directions do not render until another entry is clicked. Basically it's always one step behind the current markers. So, for 2 stops, I'll have directions from current location to stop 1, but not stop 2. For 3 stops, current location to stop 1 to stop 2 will be generated, but not stop 3.
When I log out the length of the array of stops inside of render I get the expected amount, a length of 1 for 1 stop. I have tried putting the method inside of componentWillWillReceiveProps and componentWillUpdate, and both methods will log a 0 for 1 stop.
Here's the component, if relevant:
const GoogleMapComponent = React.createClass({
mixin: [PureRenderMixin],
getInitialState: function() {
return {
map: null,
maps: null,
color: 0
}
},
componentWillUpdate: function() {
console.log('LOGS ZERO HERE', this.props.tourList.length)
if (this.state.maps) {
this.calculateAndDisplayRoute(this.state.directionsService, this.state.directionsDisplay, this.props.tourList);
}
},
saveMapReferences: function(map, maps) {
let directionsDisplay = new maps.DirectionsRenderer({map, polylineOptions: {strokeColor: '#76FF03'}, suppressMarkers: true});
let directionsService = new maps.DirectionsService();
this.setState({ map, maps, directionsService, directionsDisplay });
},
generateWaypoints: function(coords) {
return coords.map((coord) => {
return { location: new this.state.maps.LatLng(coord.lat, coord.lng) };
});
},
calculateAndDisplayRoute: function(directionsService, directionsDisplay, tourStops) {
let origin = this.props.userLocation || { lat: 37.77, lng: -122.447 };
let destination = tourStops[tourStops.length - 1];
let directions = { origin, destination, travelMode: this.state.maps.TravelMode.DRIVING };
if (this.props.tourList.length > 1) {
directions.waypoints = this.generateWaypoints(tourStops);
}
if (tourStops.length > 0) {
directionsService.route(directions, (response, status) => {
if (status === this.state.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
} else {
console.log('Directions request failed due to ' + status);
}
});
} else {
directionsDisplay.set('directions', null);
}
},
render: function() {
console.log('LOGS 1 HERE', this.props.tourList.length)
let markers = this.props.tourList.map((marker, idx) => {
let loc = marker.prevLoc ? marker.prevLoc : 'your current location.';
return <Marker className='point' key={idx} image={marker.poster} lat={marker.lat} lng={marker.lng} location={marker.location} price={marker.price} loc={loc} />
});
let defaultCenter = {lat: 37.762, lng: -122.4394};
let defaultZoom = 12
if (this.props.userLocation !== null) {
return (
<div className='map'>
<GoogleMap defaultCenter={defaultCenter} defaultZoom={defaultZoom} yesIWantToUseGoogleMapApiInternals={true}
onGoogleApiLoaded={({map, maps}) => {
map.setOptions({styles: mapStyles});
this.saveMapReferences(map, maps);
}} >
{markers}
<UserMarker lat={this.props.userLocation.lat} lng= {this.props.userLocation.lng} />
</GoogleMap>
</div>
);
}
return (
<div className='map'>
<GoogleMap defaultCenter={defaultCenter} defaultZoom={defaultZoom} yesIWantToUseGoogleMapApiInternals={true}
onGoogleApiLoaded={({map, maps}) => {
map.setOptions({styles: mapStyles});
this.saveMapReferences(map, maps);
}} >
{markers}
</GoogleMap>
</div>
);
}
});
function mapStateToProps(state) {
return {
tourList: state.sidebar.tourList,
userLocation: state.home.userLocation
}
}
export default connect(mapStateToProps)(GoogleMapComponent);
Figured it out, I was not passing nextProps to componentWillUpdate, so the function was always being called with the old props.
componentWillUpdate is called prior to this.props being updated. Change componentWillUpdate as follows:
componentWillUpdate: function(nextProps) {
console.log('SHOULD LOG ONE HERE', nextProps.tourList.length)
if (this.state.maps) {
this.calculateAndDisplayRoute(this.state.directionsService, this.state.directionsDisplay, nextProps.tourList);
}
}