I am stuck in handling future and getting distance on extracting address from Firebase and displaying on the marker,
though I have managed to write some code for getting the current user location, calculating the distance, and converting the address to LatLng but still I am facing difficulties.
Below I have attached my code and also highlighted where I want to calculate the distance( Inside widget setMapPins() )
I have stored the addresses inside collection shops and document named Address in firebase
Please help me to calculate the distance inside Streambuilder and display it on the marker. Thanks in Advance.
This is the link to my complete map.dart file
'necessary imports'
'necessary initialization'
_getCurrentLocation() async {
await _geolocator
.getCurrentPosition(desiredAccuracy: LocationAccuracy.high)
.then((Position position) async {
setState(() {
_currentPosition = position;
sourceLocation =
LatLng(_currentPosition.latitude, _currentPosition.longitude);
print('CURRENT POS: $_currentPosition');
mapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(position.latitude, position.longitude),
zoom: 14.0,
),
),
);
});
await _getAddress();
}).catchError((e) {
print(e);
});
}
// Method for retrieving the address
_getAddress() async {
try {
List<Placemark> p = await _geolocator.placemarkFromCoordinates(
_currentPosition.latitude, _currentPosition.longitude);
Placemark place = p[0];
setState(() {
_currentAddress =
"${place.name}, ${place.locality}, ${place.postalCode}, ${place.country}";
startAddressController.text = _currentAddress;
_startAddress = _currentAddress;
});
} catch (e) {
print(e);
}
}
// Method for calculating the distance between two places
Future<bool> _calculateDistance() async {
try {
// Retrieving placemarks from addresses
List<Placemark> startPlacemark =
await _geolocator.placemarkFromAddress(_startAddress);
List<Placemark> destinationPlacemark =
await _geolocator.placemarkFromAddress(_destinationAddress);
if (startPlacemark != null && destinationPlacemark != null) {
Position startCoordinates = _startAddress == _currentAddress
? Position(
latitude: _currentPosition.latitude,
longitude: _currentPosition.longitude)
: startPlacemark[0].position;
Position destinationCoordinates = destinationPlacemark[0].position;
await _createPolylines(startCoordinates, destinationCoordinates);
double totalDistance = 0.0;
// Calculating the total distance by adding the distance
// between small segments
for (int i = 0; i < polylineCoordinates.length - 1; i++) {
totalDistance += _coordinateDistance(
polylineCoordinates[i].latitude,
polylineCoordinates[i].longitude,
polylineCoordinates[i + 1].latitude,
polylineCoordinates[i + 1].longitude,
);
}
setState(() {
_placeDistance = totalDistance.toStringAsFixed(2);
return true;
}
} catch (e) {
print(e);
}
return false;
}
// formula
double _coordinateDistance(lat1, lon1, lat2, lon2) {
var p = 0.017453292519943295;
var c = cos;
var a = 0.5 -
c((lat2 - lat1) * p) / 2 +
c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p)) / 2;
return 12742 * asin(sqrt(a));
}
// Create the polylines for showing the route between two places
_createPolylines(start, destination) async {
polylinePoints = PolylinePoints();
List<PointLatLng> result = await polylinePoints.getRouteBetweenCoordinates(
googleAPIKey, // Google Maps API Key
start.latitude, start.longitude,
destination.latitude, destination.longitude,
);
if (result.isNotEmpty) {
result.forEach((PointLatLng point) {
polylineCoordinates.add(LatLng(point.latitude, point.longitude));
});
}
}
Widget setMapPins() {
return StreamBuilder(
stream: Firestore.instance.collection('shops').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Text('Loading maps... Please Wait');
for (int i = 0; i < snapshot.data.documents.length; i++) {
print(snapshot.data.documents[i]);
/// here i want to calculate distance by extracting address from Firebase and then displaying on marker
}
return Container();
});
}
#override
void initState() {
super.initState();
_getCurrentLocation();
}
To reduce the complexity of your code,you can use this function to calculate the distance between 2 points.(..in GeoLocator package)
double distanceInMeters =Geolocator().distanceBetween(lat1, long1,lat2,long2);
UPDATE 2:-
make a function to do the async task,
var distInMeters;
getDist({var latt,var longg}) async{
distanceInMeters = await Geolocator().distanceBetween(latt, longg,lat2,long2);// lat2 and long2 are global variables with current user's location
}
In your Streambuilder,
StreamBuilder(
stream: Firestore.instance.collection('shops').snapshots(),
builder: (BuildContext context,
AsyncSnapshot<List<DocumentSnapshot>> snapshots) {
if (snapshots.connectionState == ConnectionState.active &&
snapshots.hasData) {
print(snapshots.data);
return ListView.builder(
itemCount: snapshots.data.length,
itemBuilder: (BuildContext context, int index) {
DocumentSnapshot doc = snapshots.data[index];
Map yourdata= doc.data;
/*
Here, you can get latitude and longitude from firebase, using keys,based on your document structure.
Example, var lat=yourdata["latitude"];
var long=yourdata["longitude"];
Now, you can calculate the distance,
getDist(latt:lat,longg:long);
here lat2 and long2 are current user's latitude and longitude.
print(distInMeters);
*/
return Text("please upvote and accept this as the answer if it helped :) "),
);
},
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
Related
since Firebase was updated I have been having some issues, first it was because now, instead of a map, an Object? is returned and that is now fixed, but I can't seem to get any data from the database. I can put it there fine but the reading is not working.
This is on my firebase_utils .dart file
FirebaseDatabase data = FirebaseDatabase.instance;
DatabaseReference database = data.ref();
Future<void> init() async {
FirebaseAuth.instance.userChanges().listen((user) {
if (user != null) {
//_loginState = ApplicationLoginState.loggedIn;
} else {
//_loginState = ApplicationLoginState.loggedOut;
}
});
if (!kIsWeb) {
data.setPersistenceEnabled(true);
data.setPersistenceCacheSizeBytes(10000000);
}
}
I have this class:
class ReservationStreamPublisher {
Stream<List<Reservation>> getReservationStream() {
final stream = database.child('reservations').onValue;
final streamToPublish = stream
.map((event) {
List<Reservation> reservationList = [];
Map<String, dynamic>.from(event.snapshot.value as dynamic)
.forEach((key, value) => reservationList.add(Reservation.fromRTDB(value)));
print(reservationList);
return reservationList;
});
return streamToPublish;
}
}
Next is my Reservation.fromRTDB
factory Reservation.fromRTDB(Map<String, dynamic> data) {
return Reservation(
pin: data['pin'],
day: data['day'],
hour: data['hour'],
duration: data['duration'],
state: data['state'],
userEmail: data['client_email'],
completed: data['completed'],
id: data['id'],
dateMade: '',
timeMade: '');
}
And this is one of the places where I am supposed to show data
Text('Slots Reservados neste dia:'),
_selectedDate != null
? StreamBuilder(
stream:
ReservationStreamPublisher().getReservationStream(),
builder: (context, snapshot) {
final tilesList = <ListTile>[];
if (snapshot.hasData) {
List reservations =
snapshot.data as List<Reservation>;
int i = 0;
do {
if (reservations.isNotEmpty) {
if (reservations[i].day !=
(DateFormat('dd/MM/yyyy')
.format(_selectedDate!))) {
reservations.removeAt(i);
i = i;
} else
i++;
}
} while (i < reservations.length);
try {
tilesList
.addAll(reservations.map((nextReservation) {
return ListTile(
leading: Icon(Icons.lock_clock),
title: Text(
"Das ${nextReservation.hour} as ${nextReservation.duration}"),
);
}));
} catch (e) {
return Text(
'Ainda nao existem reservas neste dia');
}
}
// }
if (tilesList.isNotEmpty) {
return Expanded(
child: ListView(
children: tilesList,
),
);
}
return Text('Ainda nao existem reservas neste dia');
})
: SizedBox(),
I am not getting any error at the moment, but no data is returned.This is a Reservation example on my RTDB
I am tried a lot of method for get sub collection data from[enter image description here] cloud firestore image of database
my future builder code
FutureBuilder<BasicInfoModela?>(
future: FirstoreService()
.getDoctorbasicsDetails(id: '123456'),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return buildText("$snapshot");
} else if (snapshot.hasData) {
final data = snapshot.data;
print('called');
print(data);
final provider = Provider.of<WorKProvider>(context);
provider.setData(data!);
return body();
} else {
print('calledb');
return CircularProgressIndicator();
}
}
},
)),
my model code
factory BasicInfoModela.fromData(
Map<String, dynamic> data ) {
return BasicInfoModela(
userId: data['userId'],
firstName: data['firstname'],
lastName: data['lastname'],
email: data['email'],
contactnumber: data['contactnumber'],
dob: data['dod'],
gender: data['gender'],
city: data['city'],
state: data['state'],
country: data['coutry'],
experience: data['experience'],
speclization: data[''],
);
my future code for get data
Future<BasicInfoModela?> getDoctorbasicsDetails({String? id}) async {
try {
var dbdata = _usersCollectionReference.doc(id).snapshots();
String joinString = jsonDecode(dbdata.toString());
print(joinString);
return BasicInfoModela.fromData(jsonDecode(joinString));
} catch (e) {
if (e is PlatformException) {
}
}
}
how can read the sub collection?
in this code i get always null
i know its simple thing but i my case its note working
_usersCollectionReference.doc(id).snapshots()
this will return a Stream<DocumentSnapshot>
Try this code
Future<BasicInfoModela?> getDoctorbasicsDetails({String? id}) async {
try {
var dbdata = await _usersCollectionReference.doc(id).get();
return BasicInfoModela.fromData(dbdata.data);
} catch (e) {
if (e is PlatformException) {}
}
}
I have a collections with field of rating and i want to iterate through the collection and add all the values. Finally i want to store it in a variable that can be accessed by all screen in flutter.
Any idea?
Here what i have tried so but failed
void calculateHomeTeamRating(){
int rating=0;
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('SelectedPlayersHome').snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData){
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final players = snapshot.data.docs;
List<int> homeRating = [];
for(var playerRating in players){
rating = rating + playerRating.get('Rating');
}
String textRating = rating.toString();
homeRating.add(rating);
return null;
},
);
}
}
Here is the collections and the fields that shows the player name and their rating:
You can create a model class to encode and decode incoming data from firestore and can use this model class to store data.
Create class to store player data.
class Player {
String playerName;
int rating;
String timestamp;
Player({this.playerName, this.rating, this.timestamp});
Player.fromJson(Map<String, dynamic> json) {
playerName = json['PlayerName'];
rating = json['Rating'];
timestamp = json['timestamp'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['PlayerName'] = this.playerName;
data['Rating'] = this.rating;
data['timestamp'] = this.timestamp;
return data;
}
}
Pass QuerySnapshot to calculateHomeTeamRating function and it will returns the List<Player> and use it accordingly. .
Future<List<Player>> calculateHomeTeamRating(
QuerySnapshot querySnapshot) async {
final List<Player> playerList = [];
final data = querySnapshot.docs;
if (data != null && data.isNotEmpty) {
for (var i = 0; i < data.length; i++) {
var model = Player.fromJson(querySnapshot.docs[i].data());
playerList.add(model);
}
return playerList;
} else {
return null;
}
}
}
use specific value(rating) from list of player
var List<int> ratings;
final players = snapshot.data.docs;
final list = calculateHomeTeamRating(players);
if(list!= null && list.isNotEmpty){
ratings = list.map((player)=> player.rating).toList();
}
if(ratings!= null && ratings.isNotEmpty){
print("Total given ratings: ${ratings.length}");
}
I am making an app that calculates and shows the nearby businesses by fetching the latitude and longitude which I saved in Firebase Firestore and calculate them according to the user location and i have to put them in listview here and i wanted to sort them according to the nearest location when being calculated and fetched
import 'package:e_commerce/zRealDistance/AssistantMethods.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
var passlat;
var passlong;
#override
Future<String> getCurrentLocation(String slatitude, String slongitude) async {
var slat = double.parse(slatitude);
var slon = double.parse(slongitude);
var position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.bestForNavigation);
passlat = position.latitude;
passlong = position.longitude;
LatLng a = LatLng(passlat, passlong);
LatLng b = LatLng(slat, slon);
var details = await AssistantMethods.obtainDirectionDetails(a, b);
return details.distanceText;
}
Here is the direction detail class
class DirectionDetails {
int distanceValue;
int durationValue;
String distanceText;
String durationText;
String encodedPoints;
DirectionDetails({
this.distanceText,
this.distanceValue,
this.durationText,
this.durationValue,
this.encodedPoints
});
}
and this below calculates and obtains the direction details
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'request_assistant.dart';
import 'directiondetails.dart';
class AssistantMethods {
static Future<DirectionDetails> obtainDirectionDetails(
LatLng initialPosition, LatLng finalPosition) async {
String directionUrl =
"https://maps.googleapis.com/maps/api/directions/json?origin=${initialPosition.latitude},${initialPosition.longitude}&destination=${finalPosition.latitude},${finalPosition.longitude}&key=apiKey";
var res = await RequestAssistant.getRequest(directionUrl);
if (res == "failed") {
print(res.toString());
}
if (res == null) {
print(res.toString());
}
try {
DirectionDetails directionDetails = DirectionDetails();
directionDetails.encodedPoints =
res["routes"][0]["overview_polyline"]["points"];
directionDetails.distanceText =
res["routes"][0]["legs"][0]["distance"]["text"];
directionDetails.distanceValue =
res["routes"][0]["legs"][0]["distance"]["value"];
directionDetails.durationText =
res["routes"][0]["legs"][0]["duration"]["text"];
directionDetails.durationValue =
res["routes"][0]["legs"][0]["duration"]["value"];
return directionDetails;
} catch (exp) {
print(exp.toString());
}
}
}
and at last this one puts them in a text format in a listtile
subtitle: FutureBuilder<String>(
future: getCurrentLocation(
'${doc[index].data()['latitude']}',
'${doc[index].data()['longitude']}'),
builder: (BuildContext context,
AsyncSnapshot<String> snapshot) {
List<Widget> children;
if (snapshot.hasData) {
children = <Widget>[
Text('Distance: ${snapshot.data}'),
];
} else {
children = <Widget>[
JumpingText(
'Calculating distance...',
),
];
}
return Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: children,
);
},
),
So I'm trying to display a pie chart using the fl_chart plugin. The data for the chart is being retrieved from firestore. I have this function that is used to display the data:
List<PieChartSectionData> showSection(AsyncSnapshot<QuerySnapshot> snapshot) {
return List.generate(length, (i) {
final isTouched = i == touchedIndex;
final double fontSize = isTouched ? 25 : 16;
final double radius = isTouched ? 60 : 50;
return PieChartSectionData(
color: Color(int.parse(cerealData[i].colorVal)),
value: cerealData[i].rating,
title: cerealData[i].rating.toString(),
radius: radius,
titleStyle: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: const Color(0xffffffff)),
);
});
}
The List.generate() takes an int as an argument. Since I'm displaying realtime data, I'm trying to get the number of documents present in my collection. For that, I have a function called getLength():
void getLength(AsyncSnapshot<QuerySnapshot> snapshot) async {
length = await Firestore.instance.collection('cereal').snapshots().length;
cerealData =
snapshot.data.documents.map((e) => Cereal.fromJson(e.data)).toList();
}
However, when I run the code, I get:
Another exception was thrown: type 'Future<int>' is not a subtype of type 'int'
The entire code:
class _FlChartPageState extends State<FlChartPage> {
int touchedIndex;
var length;
List<Cereal> cerealData;
void getLength(AsyncSnapshot<QuerySnapshot> snapshot) async {
length = await Firestore.instance.collection('cereal').snapshots().length;
cerealData =
snapshot.data.documents.map((e) => Cereal.fromJson(e.data)).toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('cereal').snapshots(),
builder: (context, snapshot) {
getLength(snapshot);
if (!snapshot.hasData)
return CircularProgressIndicator();
else {
return PieChart(PieChartData(
pieTouchData: PieTouchData(touchCallback: (pieTouchResponse) {
setState(() {
if (pieTouchResponse.touchInput is FlLongPressEnd ||
pieTouchResponse.touchInput is FlPanEnd) {
touchedIndex = -1;
} else {
touchedIndex = pieTouchResponse.touchedSectionIndex;
}
});
}),
borderData: FlBorderData(show: false),
sectionsSpace: 0,
centerSpaceRadius: 40,
sections: showSection(snapshot)));
}
}),
);
}
List<PieChartSectionData> showSection(AsyncSnapshot<QuerySnapshot> snapshot) {
return List.generate(length, (i) {
final isTouched = i == touchedIndex;
final double fontSize = isTouched ? 25 : 16;
final double radius = isTouched ? 60 : 50;
return PieChartSectionData(
color: Color(int.parse(cerealData[i].colorVal)),
value: cerealData[i].rating,
title: cerealData[i].rating.toString(),
radius: radius,
titleStyle: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: const Color(0xffffffff)),
);
});
}
}
I read somewhere that awaiting the future gets rid of the Future. But that doesn't work here.
How do I fix this?
Edit: It works if I simply pass the number of documents instead of length in List.generate(). But this won't work if there are changes to the collection. So how do I convert Future to int?
I think you aren't getting the length of the documents, you are getting the length of the snapshots if you want to get the documents length :
QuerySnapshot querySnapshot = await Firestore.instance.collection('cereal').getDocuments();
int length = querySnapshot.documents.length;
In get getLength function you are trying to get length which is actually async task which returns future and because of that you are getting following error.
Change your method with following metod
getLength()async{
Firestore.instance.collection('cereal').snapshots().length.then((len){
length = len;
cerealData =
snapshot.data.documents.map((e) => Cereal.fromJson(e.data)).toList();
});
}