How to access widget in an initializer in google maps flutter - flutter

I am passing select latitude and longitude from google maps to my flutter app's another screen where I am calculating the distance between 2 locations.
I've got the values coming fine but there's this widget can't be accessed in an initializer issue coming.
I'm using google maps and I've to pass the widget.lat widget.long values to the userLocation marker.
I'm using this tutorial's code by the way
Get distance between locations
Here's my code
class CalculateDistance extends StatefulWidget {
const CalculateDistance({super.key, required this.lang, required this.lat});
final double lang;
final double lat;
#override
// ignore: library_private_types_in_public_api
_CalculateDistanceState createState() => _CalculateDistanceState();
}
class _CalculateDistanceState extends State<CalculateDistance> {
GoogleMapController? mapController; //contrller for Google map
PolylinePoints polylinePoints = PolylinePoints();
String googleAPiKey = "YOUR_API_KEY";
Set<Marker> markers = {}; //markers for google map
Map<PolylineId, Polyline> polylines = {}; //polylines to show direction
LatLng storeLocation =
const LatLng(-30.600164342582726, 23.508854043469647); // Store location
// This is where I can't use the passed values
LatLng userLocation = LatLng(widget.lat, widget.lang); // User location
double distance = 0.0;
#override
void initState() {
markers.add(Marker(
//add start location marker
markerId: MarkerId(storeLocation.toString()),
position: storeLocation, //position of marker
infoWindow: const InfoWindow(
//popup info
title: 'Store Location',
snippet: 'Store Marker',
),
icon: BitmapDescriptor.defaultMarker, //Icon for Marker
));
markers.add(Marker(
//add distination location marker
markerId: MarkerId(userLocation.toString()),
position: userLocation, //position of marker
infoWindow: const InfoWindow(
//popup info
title: 'User Location',
snippet: 'User Marker',
),
icon: BitmapDescriptor.defaultMarker, //Icon for Marker
));
getDirections(); //fetch direction polylines from Google API
super.initState();
}
getDirections() async {
List<LatLng> polylineCoordinates = [];
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
googleAPiKey,
PointLatLng(storeLocation.latitude, storeLocation.longitude),
PointLatLng(userLocation.latitude, userLocation.longitude),
travelMode: TravelMode.driving,
);
if (result.points.isNotEmpty) {
for (var point in result.points) {
polylineCoordinates.add(LatLng(point.latitude, point.longitude));
}
} else {
print(result.errorMessage);
}
//polulineCoordinates is the List of longitute and latidtude.
double totalDistance = 0;
for (var i = 0; i < polylineCoordinates.length - 1; i++) {
totalDistance += calculateDistance(
polylineCoordinates[i].latitude,
polylineCoordinates[i].longitude,
polylineCoordinates[i + 1].latitude,
polylineCoordinates[i + 1].longitude);
}
print(totalDistance);
setState(() {
distance = totalDistance;
});
//add to the list of poly line coordinates
addPolyLine(polylineCoordinates);
}
addPolyLine(List<LatLng> polylineCoordinates) {
PolylineId id = const PolylineId("poly");
Polyline polyline = Polyline(
polylineId: id,
color: Colors.deepPurpleAccent,
points: polylineCoordinates,
width: 8,
);
polylines[id] = polyline;
setState(() {});
}
double calculateDistance(lat1, lon1, lat2, lon2) {
var p = 0.017453292519943295;
var a = 0.5 -
cos((lat2 - lat1) * p) / 2 +
cos(lat1 * p) * cos(lat2 * p) * (1 - cos((lon2 - lon1) * p)) / 2;
return 12742 * asin(sqrt(a));
}
// Scaffold ahead

Declare this LatLng with late keyword as a member of _CalculateDistanceState class:
class _CalculateDistanceState extends State<CalculateDistance> {
late LatLng _userLocation;
(...)
}
Then in the initState you will have access to the widget:
void initState() {
super.initState();
_userLocation = LatLng(widget.lat, widget.lang);
(...)
}

Related

How to use MapQuest Directions API To Draw Rout From One Location To Another in Flutter

I'm currently developing a flutter a taxi application and I want to use MapQuest API for the applications to get the locations names and to draw route from one point to another.
The problem is that calling the API and getting the locations works fine but the route is showing on the map in a really weird way.
Here is the provider code for calling the API and getting the route locations points:
class MapQuestDataProvider {
Future<MapQuestDirection> getDirections(
LatLng Source, LatLng Destination) async {
try {
MapQuestDirection result = MapQuestDirection(locations: []);
var aPIKey = "[API_KEY]";
var url =
"https://www.mapquestapi.com/directions/v2/optimizedroute?key=$aPIKey&json={\"locations\":[\"${Source.latitude},${Destination.longitude}\",\"${Destination.latitude},${Source.longitude}\"]}";
var uri = Uri.parse(url);
final response = await http.get(uri);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
var route = data["route"];
var legs = route["legs"];
var zero = legs[0];
var maneuvers = zero["maneuvers"] as List;
for (var element in maneuvers) {
var startPoint = element["startPoint"];
var lat = startPoint["lat"];
var lng = startPoint["lng"];
result.locations.add(LatLng(lat, lng));
}
return result;
} else {
throw Exception('Failed to load Locations');
}
} catch (e) {
print("Exception throuwn $e");
throw e;
}
}
}
Here is the code for my Map Widget:
class MapWidget extends StatefulWidget {
MapWidget(
{Key? key,
this.currentLocation,
required this.onTab,
required this.markers,
this.endLocation,
required this.polylines})
: super(key: key);
LatLng? currentLocation;
void Function(LatLng) onTab;
Set<Marker> markers = {};
LatLng? endLocation;
Map<PolylineId, Polyline> polylines = {};
#override
State<MapWidget> createState() => _MapWidgetState();
}
class _MapWidgetState extends State<MapWidget> {
GoogleMapController? mapController;
final defaultMapZooming = 18.0;
#override
Widget build(BuildContext context) {
return Container(child: mapView(context));
}
Widget mapView(BuildContext context) {
if (mapController != null && widget.currentLocation != null) {
mapController!.animateCamera(CameraUpdate.newLatLngZoom(
LatLng(widget.currentLocation!.latitude,
widget.currentLocation!.longitude),
defaultMapZooming));
}
var map = GoogleMap(
onMapCreated: onMapCreated,
zoomControlsEnabled: false,
myLocationEnabled: false,
mapType: MapType.normal,
onLongPress: (loc) {
widget.onTab(loc);
},
markers: widget.markers,
polylines: widget.polylines.values.toSet(),
minMaxZoomPreference: const MinMaxZoomPreference(12, 25),
initialCameraPosition: CameraPosition(
target: getLocationOrDefault(),
zoom: defaultMapZooming,
),
onCameraMove: (CameraPosition cameraPosition) {
print("ZOOOOOOOOOOOOOOOOOOOM IS : " + cameraPosition.zoom.toString());
},
);
return map;
}
onMapCreated(GoogleMapController controller) {
mapController = controller;
}
getLocationOrDefault() {
return widget.currentLocation ??
const LatLng(21.215415017175165, 72.8879595194489);
}
}
And Here is the code that gets called by placing a new marker on the map which draws the polygon that will be sent to the Map Widget after state update:
SetPathFromMapQuestData(List<LatLng> Locations) {
PolylinePoints polylinePoints = PolylinePoints();
Set<Marker> markers = Set(); //markers for google map
LatLng startLocation = this.startLocation!;
LatLng endLocation = this.endLocation!;
double distance = 0.0;
List<LatLng> polylineCoordinates = [];
//polylineCoordinates.add(this.startLocation!);
Locations.forEach((LatLng point) {
polylineCoordinates.add(point);
});
polylineCoordinates.insert(0, this.startLocation!);
polylineCoordinates.add(this.endLocation!);
PolylineId id = PolylineId("poly");
var poly = GetPloylineId(polylineCoordinates, id);
polylines[id] = poly;
setState(() {
this.polylines = polylines;
});
}
Polyline GetPloylineId(List<LatLng> polylineCoordinates, PolylineId id) {
Polyline polyline = Polyline(
polylineId: id,
color: Colors.deepPurpleAccent,
points: polylineCoordinates,
width: 8,
);
return polyline;
}
This is the result:
The Result Map Image
After working for hours I moved to MapBox and it works perfectly.
Here is the link for the Api:
https://docs.mapbox.com/android/navigation/guides/

How to add user info above location marker in flutter

I want to display drivers' info above the markers on the map, like driver name and car name. but i do not know how to do so.
I am using flutter, flutter geofire and firebase realtime database
My code is
displayActiveDriversOnUserMap()
{ setState((){
markersSet.clear();
circleSet.clear();
Set<Marker> driversMarkerSet = Set<Marker>();
for(ActiverNearbyAvailableDrivers eachDriver in GeoFireAssistant.activeNearbyAvailableDriversList)
{
LatLng eachDriverActivePosition = LatLng(eachDriver.locationLatitude!, eachDriver.locationLongitude!);
Marker marker = Marker(
markerId: MarkerId(eachDriver.driverId!),
position: eachDriverActivePosition,
icon: activeNearByIcon!,
rotation: 360,
);
driversMarkerSet.add(marker);
}
setState ((){
markersSet = driversMarkerSet;
});
});
}
createActiveNearByDriverIconMarker()
{
if(activeNearByIcon == null)
{
ImageConfiguration imageConfiguration = createLocalImageConfiguration(context, size: const Size(2, 2));
BitmapDescriptor.fromAssetImage(imageConfiguration, "images/car.png").then((value)
{
activeNearByIcon = value;
});
}
}
}
screenshot of map

I have a flutter function to add markers to GoogleMaps with text from the Firestore database

I am able to add the markers to the map but when I try to add the name of the place in the infowindow, only one name appears on all the infowindows. My intention is to have a different name for each infowindow.
Here's the code for the function
_handleTapOrig() {
LatLng tappedPoint;
setState(() {
FirebaseFirestore.instance.collection('bus_stages').get().then((value) {
if (value.docs.isNotEmpty) {
for (int i = 0; i < value.docs.length; i++) {
DocumentSnapshot snap = value.docs[i];
GeoPoint geoPoint = snap.get("location");
double mylat = geoPoint.latitude;
String jje = snap.get("stage_name");
double mylng = geoPoint.longitude;
LatLng mypoint = LatLng(mylat, mylng);
setState(() {
_mylatlng.add(LatLng(mylat, mylng)
);
});
///Add markers
myMarker = [];
_mylatlng.forEach((element) {
tappedPoint = element;
myMarker.add(Marker(
markerId: MarkerId(tappedPoint.toString()),
position: tappedPoint,
icon: myBitmapDescriptor,
/// BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueCyan),
infoWindow: InfoWindow(title: "Bus Stage ${snap.get("stage_name")}",
snippet: 'Tap Google directions button to navigate to stage'),
));
});
}
// print(_mylatlng);
}
});
// print('List of Markers: $myMarker');
});
}

Flutter : Mapbox symbol on click open google maps

I have project using MapBox to show address location in map. I have latitude and longitude (-6.192461941069894,106.97593586545025) like this , i want if i click right on marker, i want open Google Maps Apps based on latitude and longitude i have.
But the problem , i can't open google maps after click symbol because latitude&longitude after click maps not same with latitude&longitude i have.
Logic Source Code
onMapClick: (point, latlng) {
if (latlng.latitude == latitude && latlng.longitude == longitude) {
launchGoogleMaps(latitude: latitude, longitude: longitude);
}
print(
"From Map ${latlng.latitude} |${latlng.latitude} \nFrom Server $latitude||$longitude \n\n");
},
I think when clicking points on the map are close to each other, I can directly open the Google Maps application. How i can do this ?
Full Source Code
class ExampleMapBox extends StatefulWidget {
#override
_ExampleMapBoxState createState() => _ExampleMapBoxState();
}
class _ExampleMapBoxState extends State<ExampleMapBox> {
MapboxMapController mapController;
double latitude, longitude;
#override
void initState() {
super.initState();
latitude = -6.192461941069894;
longitude = 106.97593586545025;
}
void _onMapCreated(MapboxMapController mapboxMapController) {
mapController = mapboxMapController;
}
#override
Widget build(BuildContext context) {
return MapboxMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(target: LatLng(latitude, longitude), zoom: 10),
onStyleLoadedCallback: () => addSymbol(mapController),
onMapClick: (point, latlng) {
if (latlng.latitude == latitude && latlng.longitude == longitude) {
launchGoogleMaps(latitude: latitude, longitude: longitude);
}
print(
"From Map ${latlng.latitude} |${latlng.latitude} \nFrom Server $latitude||$longitude \n\n");
},
);
}
void addSymbol(MapboxMapController mapBoxController) {
mapBoxController.addSymbol(
SymbolOptions(
geometry: LatLng(latitude, longitude),
iconImage: "assets/images/custom-icon.png",
iconSize: 2,
),
);
}
void launchGoogleMaps({#required double latitude, #required double longitude}) async {
String googleUrl = 'https://www.google.com/maps/search/?api=1&query=$latitude,$longitude';
if (await canLaunch(googleUrl)) {
await launch(googleUrl);
} else {
throw 'Could Not Open The Map';
}
}
}
Generating the exact same coordinates up to a 15 digit precision by clicking on a map is far from possible.
I would recommend to compute the deviation in latitude and longitude and fire the launchGoogleMaps function when the deviation is below a certain threshold. Please try something like the below:
deviation_lat = (latlng.latitude - latitude) * (latlng.latitude - latitude);
deviation_lon = (latlng.longitude - longitude) * (latlng.longitude - longitude);
if (deviation_lat < 1 && deviation_lon < 1) {
launchGoogleMaps(latitude: latitude, longitude: longitude);
}
aFunction(Symbol symbol){
// code goes here
}
void addSymbol(MapboxMapController mapBoxController) {
mapBoxController.addSymbol(
SymbolOptions(
geometry: LatLng(latitude, longitude),
iconImage: "assets/images/custom-icon.png",
iconSize: 2,
),
);
mapBoxController.onSymbolTapped.add(aFunction); // This is a callback for symbols when tapped.
}
You can try this.

Cant Update Array With Dropdown Button And Future Flutter

I am using a dropdown button to update locations of restaurants around a user but, the locations aren't updating in the ListView. Here is what I have so far:
Edit: Added Init statement
Init Statement
#override
void initState() {
_dropDownMenuItems = buildAndGetDropDownMenuItems(changeDistance);
_mapDropdownFilter = _dropDownMenuItems[0].value;
filterMarker(userLat, userLong, dispensaries, 1);
super.initState();
}
I set up a class to assist with this called locations
class Locations{
final Restaurant dis;
final double dist;
final int index;
Locations({this.dis, this.dist, this.index});
}
Then call this filter to sort my location by distance and give them a distance in miles from user:
Future filterMarker(userLat, userLong, restaurants, filterdis) async {
Set<Marker> _tmpMarkers = Set();
final Uint8List markerIcon =
await getBytesFromAsset('assets/smiley.png', 100);
int filterCounter = 0;
List<Locations> _tmpLocation = List();
for (int i = 0; i < restaurant.length; ++i) {
Geolocator()
.distanceBetween(
userLat, userLong, restaurants[i].lat, restaurants[i].long)
.then((calcDist) {
print(calcDist);
if (calcDist / 1000 < filterdis) {
filterCounter++;
final String markerIdVal = 'marker_id_$filterCounter';
final MarkerId markerId = MarkerId(markerIdVal);
var dis = calcDist / 1000;
var distances = dis.toStringAsFixed(2);
// creating a new MARKER
_tmpMarkers.add(Marker(
markerId: markerId,
position: LatLng(restaurants[i].lat, restaurants[i].long),
infoWindow: InfoWindow(
title: restaurants[i].name, snippet: '$distances mi Away'),
icon: BitmapDescriptor.fromBytes(markerIcon)));
_tmpLocation.add(
Locations(index: i, dis: restaurants[i], dist: calcDist / 1000));
}
});
setState(() {
_filteredMarkers = _tmpMarkers;
filteredVenues = _tmpLocation.cast<Locations>();
});
print('Do once');
}
print('There are ${filteredVenues.length} _filteredMarkers');
}
My drop down button button is setup like this:
DropdownButton(
style: TextStyle(
fontFamily: 'Roboto-Regular',
color: Colors.black),
elevation: 6,
icon: Icon(
FontAwesomeIcons.angleRight,
color: buddiesGreen,
),
value: _mapDropdownFilter,
items: _dropDownMenuItems,
onChanged: _changeFilterList,
)
Filled with this:
//Distance Map filter Button
final List changeDistance = ['1', '5 ', '10 ', '20'];
// Set<Marker> _filteredMarkers = Set();
List<Locations> filteredVenues;
var _filteredMarkers;
var filteredRestaurant;
_dropDownMenuItems = buildAndGetDropDownMenuItems(changeDistance);
_mapDropdownFilter = _dropDownMenuItems[0].value;
String _mapDropdownFilter;
List<DropdownMenuItem<String>> _dropDownMenuItems;
List<DropdownMenuItem<String>> buildAndGetDropDownMenuItems(List tempList) {
List<DropdownMenuItem<String>> items = List();
for (String x in tempList) {
items.add(DropdownMenuItem(
value: x,
child: Text(
x,
style: TextStyle(
fontSize: 14,
fontFamily: 'Roboto-Regular',
fontWeight: FontWeight.w700,
color: buddiesPurple),
)));
}
return items;
}
void _changeFilterList(String value) {
//use _filterDropdown for switch statement
setState(() {
_mapDropdownFilter = value;
});
_sortProductsDropDown(_mapDropdownFilter);
print(_mapDropdownFilter);
}
void _sortProductsDropDown(_mapDropdownFilter) {
switch (_mapDropdownFilter) {
case '1 Mile':
print('1 Mile');
filterMarker(userLat, userLong, restaurant, 1);
print(restaurant.length);
break;
case '5 Miles':
filterMarker(userLat, userLong, restaurant, 5);
print(restaurant.length);
break;
case '10 Miles':
filterMarker(userLat, userLong, restaurant, 10);
print(restaurant.length);
break;
case '20 Miles':
filterMarker(userLat, userLong, restaurant, 20);
print(restaurant.length);
break;
}
}
It doesn't update when I hit the dropdown where is the disconnect and why doesn't it up date when the user selects different distances.
because you're not using setState() in initState() where you're fetching the list of locations. So the state of Locations isn't updated even though you fetch everything correctly.
..
setState(){
_dropDownMenuItems = buildAndGetDropDownMenuItems(changeDistance);
..
}
do something like this.