#firebase #flutter
#firebase #flutter
Вопрос:
Я сохранил широту и долготу для элементов в базе данных firestore (поля: item_latitude и item_longitude). Таким образом, все элементы имеют широту и долготу. Я могу использовать поток для получения элементов, например:
Stream<QuerySnapshot> getItems() async* {
yield* FirebaseFirestore.instance.collection("items").snapshots();
}
Используя StreamBuilder или FutureBuilder, я могу получить отдельные свойства элементов, такие как широта и долгота. У Geolocator есть метод вычисления расстояния, который также является будущим:
double distance = await geolocator.distanceBetween(lat, long, lat1, long1);
Я могу получить текущее местоположение пользователей, и в данном случае это lat1, long1 (что является отдельной записью). Проблема в том, что Strem GetItems получает поток широт и долгот, и для каждого элемента мне нужно рассчитать его текущее расстояние по отношению к текущему местоположению. Это означает, что при переборе элементов, например, в GridView, мне нужно рассчитать и отобразить расстояние.
Я написал этот вопрос абстрактно, чтобы в ответе было указано, как выполнить асинхронное вычисление на основе синхронного потока данных, чтобы, хотя данные отображаются в разделе сборки страницы, вычисления выполняются снаружи, потому что, поскольку это не так, сборка не будет принимать асинхронное вычисление с синхронным.
Мои попытки привели к следующему: Первая попытка:
child: StreamBuilder(
stream: FetchItems().getItems(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("KE");
}
if (snapshot.hasData) {
DocumentSnapshot data = snapshot.data.docs[index];
double lat = data.data()[Str.ITEM_LATITUDE];
double long = data.data()[Str.ITEM_LATITUDE];
return
Text(getDistance(usersCurrentLocationLat,usersCurrentLocationLong,lat,long).toString());
//This fails and returns on the Text place holder the following: Instance of 'Future<dynamic>'
}
}),
Моя вторая попытка заключается в следующем:
child: StreamBuilder(
stream: FetchItems().getItems(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("KE");
}
if (snapshot.hasData) {
DocumentSnapshot data = snapshot.data.docs[index];
double lat = data.data()[Str.ITEM_LATITUDE];
double long = data.data()[Str.ITEM_LATITUDE];
double x = getDistance(usersCurrentLocationLat,usersCurrentLocationLong,lat,long);
return Text(x.toString());
//This fails and gives erro: type 'Future<dynamic>' is not a subtype of type 'double'
}
}),
Дальнейшее исследование показывает, что приведенный ниже метод, который используется для получения текущего местоположения и также упоминается в iniState, действительно получает значения (при условии, что Gps включен вне курса):
_getUserCurrentLocation() {
final Geolocator geolocator = Geolocator()..forceAndroidLocationManager;
geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.best).then(
(Position position) {
setState(
() {
_currentPosition = position;
usersCurrentLocationLat = _currentPosition.latitude;
usersCurrentLocationLong = _currentPosition.longitude;
//a system print here returns current location as 0.3714267 32.6134379 (same for the
//StreamBuilder)
},
);
},
).catchError((e) {
print(e);
});
}
Ниже приведен метод вычисления расстояния — с использованием предоставленного метода Geolocator distanceBetween().
getDistance(double lat, double long, double lat1, double long1) async {
return distance = await geolocator.distanceBetween(lat, long, lat1, long1);
}
@override
void initState() {
super.initState();
_getUserCurrentLocation();
}
Как я смогу перебирать элементы, получая их широту и долготу, вычислять расстояние и отображать его в тексте? Это общий вопрос, возможные комбинации решений будут очень приветствоваться. Обратите внимание, что в StreamBuilder я действительно могу печатать для консоли координаты для каждого, используя приведенное ниже:
print("FROM CURRENT LOCATION HERE ----" usersCurrentLocationLat.toString() "::::::::"
usersCurrentLocationLong.toString());
print("FROM STREAM FROM DB SURE ----" lat.toString() "::::::::" long.toString());
Который печатает в консоли для всех элементов в базе данных как (один пример):
I/flutter (30351): FROM CURRENT LOCATION HERE ----0.3732317::::::::32.6128083
I/flutter (30351): FROM STREAM FROM DB SURE ----2.12323::::::::2.12323
Доказательство того, что координаты действительно получены.
Основная ошибка: тип ‘Future’ не является подтипом типа ‘double’, сохраняется, а текст для отображения расстояния вытянут красным. Руководство, если можно, по наилучшему подходу — это может помочь кому-то и в будущем.
Комментарии:
1. что
index
используется вsnapshot.data.docs[index]
? какой диапазон значений можно использовать для этого?2. для потока, FetchItems().GetItems(), для каждого снимка мы выполняем итерацию с индексом, подобным: DocumentSnapshot data = snapshot.data.docs[index]; откуда берется индекс: StaggeredGridView.countBuilder( itemBuilder: (BuildContext context, int index) Элементы отображаются в виде сетки. Проблема в том, что тип ‘Future’ не является подтипом типа ‘double’ — как в том, как получить future в getDistance (поскольку он имеет await) и передать то же самое, что и double в текстовом отображении.
3. чему равно значение
index
? это 0, 100, 10000? какой диапазон значений можно использовать для этого?4. в сетке мы сначала получаем количество, поэтому, если элементов 3 или 4 или n, мы выполняем итерацию и показываем то, что мы хотим. ItemCount: элементы. передается длина: StaggeredGridView.countBuilder( scrollDirection: Axis.vertical, shrinkWrap: true, контроллер: ScrollController, crossAxisCount: 4, ItemCount: элементы. длина, itemBuilder: (контекст BuildContext, индекс int)
5. итерация с индексом работает отлично, поэтому вывод на консоль работает идеально, так что, если в моем приложении я делаю выбор из разных элементов, геокоординаты будут отображаться на консоли, а не в тексте (вычисление расстояния не работает в основном потому, что оно ожидает будущего.
Ответ №1:
Мой подход заключается в следующем:
Запрашивайте все документы следующим образом:
//This a synchronus operation
final data = await Firestore.instance
.collection('collection_name')
.getDocuments();
Затем передайте все документы в список:
Поскольку DocumentSnapshot
является List<dynamic>
List doc = data.documents;
теперь вычислите расстояние для каждого LatLng
в вашем firestore
текущем местоположении LatLng
и продолжайте добавлять его к пустому, list
которое на итерации вы можете использовать в gridView
:
List distanceList=[]; //define and empty list
doc.forEach((e){
double lat=e.data[ITEM_LATITUDE]; //asuming ITEM_LATITUDE is the field name in firestore doc.
double lng=e.data[ITEM_LONGITUDE];//asuming ITEM_LONGITUDE is the field name in firestore doc.
distanceList.add(your distance calculating function); //call this inside an async function if you are using await;
});
Вместо использования асинхронности gelocator.distance
я написал функцию в dart
, используя формулу ‘haversine’, чтобы найти кратчайшую функцию:
double calculateDistance (double lat1,double lng1,double lat2,double lng2){
double radEarth =6.3781*( pow(10.0,6.0));
double phi1= lat1*(pi/180);
double phi2 = lat2*(pi/180);
double delta1=(lat2-lat1)*(pi/180);
double delta2=(lng2-lng1)*(pi/180);
double cal1 = sin(delta1/2)*sin(delta1/2) (cos(phi1)*cos(phi2)*sin(delta2/2)*sin(delta2/2));
double cal2= 2 * atan2((sqrt(cal1)), (sqrt(1-cal1)));
double distance =radEarth*cal2;
return (distance);
}
Это synchronous function
и код внутри firestore
doc.forEach();
функции списка будет выглядеть следующим образом:
List distanceList; //define and empty list
doc.forEach((e){
double lat=e.data[ITEM_LATITUDE]; //asuming ITEM_LATITUDE is the field name in firestore doc.
double lng=e.data[ITEM_LONGITUDE];//asuming ITEM_LONGITUDE is the field name in firestore doc.
double distance = calculateDistance(currentLat,currentLng, lat,lng);
distanceList.add(distance);
});
//Now the distanceList would contain all the shortest distance between
// current LatLng and all the other LatLng in your firestore documents:
Не забудьте очистить distanceList
перед использованием, чтобы снова сохранить расстояния.
Весь код будет выглядеть следующим образом;
//Shortest Distance Function definition:
double calculateDistance (double lat1,double lng1,double lat2,double lng2){
double radEarth =6.3781*( pow(10.0,6.0));
double phi1= lat1*(pi/180);
double phi2 = lat2*(pi/180);
double delta1=(lat2-lat1)*(pi/180);
double delta2=(lng2-lng1)*(pi/180);
double cal1 = sin(delta1/2)*sin(delta1/2) (cos(phi1)*cos(phi2)*sin(delta2/2)*sin(delta2/2));
double cal2= 2 * atan2((sqrt(cal1)), (sqrt(1-cal1)));
double distance =radEarth*cal2;
return (distance);
}
List distanceList; //list defination
// Call this function every time you want to calculate distance between currentLocation and all location in firestore.
void calculateDistanceAndStore(double currentLat, double currentLng) async{
distanceList=[];p;
final data = await Firestore.instance
.collection('collection_name')
.getDocuments();
doc.forEach((e){
double lat=e.data[ITEM_LATITUDE]; //asuming ITEM_LATITUDE is the field name in firestore doc.
double lng=e.data[ITEM_LONGITUDE];//asuming ITEM_LONGITUDE is the field name in firestore doc.
double distance = calculateDistance(currentLat,currentLng, lat,lng);
distanceList.add(distance);
});
}
Комментарии:
1. позвольте мне попробовать это — я смотрю на это. минута, и я проверю, пожалуйста.
2. @ombiro попробуйте новый код, я исправил ошибки, и расстояние указано в метрах
3. не могли бы вы указать, как расстояние будет передано текстовому виджету?
4. таким образом
Text('${documentList[0]}',)
, где 0 будет вызывать первое расстояние, а 1 будет вторым элементом списка.5. Используете ли вы
ListView
для отображения расстояний?