I'm trying to load the geo location first. Then I use this value to start a query which events are in a certain radius from this location.
I want to display these events in a FutureBuilder.
My problem: I have to initialize Future<List> futureEvents in the onInit state, otherwise Flutter complains. At the time he didn't have the location yet.
So I call the function again at the end.
So it happens that the error "RangeError (index): Invalid value: Valid value range is empty :1" is thrown until the method is called again after receiving the geo-location and I get the data.
Sorry for bad coding. One of my first Projects
The InitState:
class _LocationPageState extends State<LocationPage> {
String? _currentAddress;
Position? _currentPosition;
late Future<List<Events>> futureEvents;
double locationRadius = 5;
@override
void initState() {
super.initState();
_getCurrentPosition();
futureEvents = _getEvents();
}
The called functions:
Future<void> _getCurrentPosition() async {
final hasPermission = await _handleLocationPermission();
if (!hasPermission) return;
await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high)
.then((Position position) {
setState(() => _currentPosition = position);
_getAddressFromLatLng(_currentPosition!);
}).catchError((e) {
debugPrint(e);
});
}
Future<void> _getAddressFromLatLng(Position position) async {
await placemarkFromCoordinates(
_currentPosition!.latitude, _currentPosition!.longitude)
.then((List<Placemark> placemarks) {
Placemark place = placemarks[0];
setState(() {
_currentAddress = ' ${place.postalCode} ${place.locality} ';
});
currentPLZ = place.postalCode.toString();
futureEvents = _getEvents() as Future<List<Events>>;
}).catchError((e) {
debugPrint(e);
});
}
Future<List<Events>> _getEvents() async {
// get all PLZ in a radius
final response = await http.get(Uri.parse(
'https://www.suche-postleitzahl.org/geo-api.json?action=plz-umkreis&subaction=umkreis-osm&plz='
currentPLZ
'&radius=' locationRadius.toString()));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
var jsondataPlz = jsonDecode(response.body);
List<PLZData> plzdataList = [];
for (var u in jsondataPlz) {
PLZData plzData = PLZData(u['postal_code'], u['name'], u['distance']);
plzdataList.add(plzData);
}
print(plzdataList.length);
print(plzdataList[1].name
" Distanz:"
plzdataList[1].distance
" PLZ only"
plzdataList[1].postal_code);
// get all events in the radius
List<Events> events = [];
if (plzdataList.isNotEmpty) {
for (var i = 0; plzdataList.length > i; i ) {
var singleEvent = await FirebaseFirestore.instance
.collection('Events')
.where('postCode', isEqualTo: plzdataList[i].postal_code)
.get();
if (singleEvent.docs.isNotEmpty) {
var singleEventList =singleEvent.docs.map((d) => Events.fromJson(d.data())).toList();
//add distance and regionname
for(var j = 0; singleEventList.length > j; j ){
singleEventList[j].distance = plzdataList[i].distance;
singleEventList[j].regionName = plzdataList[i].name;
}
events = events singleEventList;
if (events[0].userID != null) {
print(events[0].userID);
print(events[i].distance);
}
}
}
}
//get userdata to the events
if (events.isEmpty) {
print("Es wurden keine Events gefunden");
} else {
for (var i = 0; events.length > i; i ) {
var userInformationSnap = await FirebaseFirestore.instance
.collection('users')
.where('__name__', isEqualTo: events[i].userID)
.get();
events[i].userInformation = userInformationSnap.docs
.map((d) => UsersForPosts.fromJson(d.data()))
.toList();
print(events[i].userInformation[0].username);
}
}
return events;
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load');
}
}
The FutureBuilder
FutureBuilder<List<Events>>(
future: futureEvents,
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), //<--here
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Eventcard(
userPhoto: snapshot.data?[index].userInformation[0]
.userPhoto ?? "keine Info",
age: snapshot.data?[index].userInformation[0].age ??
"keine Info",
username: snapshot.data?[index].userInformation[0]
.username ?? "keine Info",
gender: snapshot.data?[index].userInformation[0]
.gender ?? "keine Info",
meetState: snapshot.data?[index].meetState ??
"keine Info",
postCode: snapshot.data?[index].postCode ??
"keine Info",
distance: snapshot.data?[index].distance ??
"keine Info",
regionName: snapshot.data?[index].regionName ??
"keine Info",
comment: snapshot.data?[index].comment ??
"keine Info",
headline: snapshot.data?[index].headline ??
"keine Info",
);
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
} else {
// By default, show a loading spinner.
return const CircularProgressIndicator();
}
},
),
CodePudding user response:
Have you tried to just put the _getEvents() Future
inside the FutureBuilder
directly instead of using it as a late variable? I’m also confused by this format… why use a Future
and a FutureBuilder
? It seems like you could just create an empty list events = []
then in the initState
call a new async function where you fetch the events from Firestore
and use that data to update the events
list through setState
. Then take away the FutureBuilder
and just use a ListView.builder
. Just search how to create a ListView from a list, it’s very simple.