Flutter:加载地理位置后用FutureBuilder查询Firebase引发索引越界错误
问题描述
我想先加载用户地理位置,再根据该位置查询指定半径范围内的事件,最后用FutureBuilder展示这些事件。但遇到以下问题:
- 必须在
initState中初始化Future<List<Events>> futureEvents,否则Flutter报错,但此时还未获取到地理位置。 - 获取到位置后重新调用查询函数,但在拿到地理位置并重新获取数据前,会抛出错误:
RangeError (index): Invalid value: Valid value range is empty :1
(注:这是我的第一个项目,代码可能不够规范)
初始化代码(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(); } }
调用的函数
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'); } }
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(); } }, ),
解决方案
1. 延迟初始化futureEvents,等待地理位置获取完成
initState中直接调用_getEvents()时,currentPLZ还未赋值,会导致API请求参数错误,后续解析数组触发越界。改为先获取位置再初始化:
@override void initState() { super.initState(); _initData(); } Future<void> _initData() async { await _getCurrentPosition(); if (currentPLZ.isNotEmpty) { setState(() { futureEvents = _getEvents(); }); } }
2. 修复_getEvents()中的数组越界问题
直接访问plzdataList[1]和events[i]时未判断长度,容易触发错误,添加长度校验:
// 替换原print(plzdataList[1]...)的代码 if (plzdataList.length >= 2) { print(plzdataList[1].name + " Distanz:" + plzdataList[1].distance + " PLZ only" + plzdataList[1].postal_code); } // 替换原print(events[i].distance)的代码 if (i < events.length) { print(events[i].distance); }
3. 优化FutureBuilder的状态判断与空值处理
简化状态判断逻辑,同时处理数据为空的场景,避免潜在的空指针:
FutureBuilder<List<Events>>( future: futureEvents, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } else if (snapshot.hasError) { return Text('加载失败: ${snapshot.error}'); } else if (snapshot.hasData) { final events = snapshot.data!; if (events.isEmpty) { return const Text("暂无事件"); } return ListView.builder( scrollDirection: Axis.vertical, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: events.length, itemBuilder: (context, index) { final event = events[index]; final userInfo = event.userInformation?.firstOrNull; return Eventcard( userPhoto: userInfo?.userPhoto ?? "keine Info", age: userInfo?.age ?? "keine Info", username: userInfo?.username ?? "keine Info", gender: userInfo?.gender ?? "keine Info", meetState: event.meetState ?? "keine Info", postCode: event.postCode ?? "keine Info", distance: event.distance ?? "keine Info", regionName: event.regionName ?? "keine Info", comment: event.comment ?? "keine Info", headline: event.headline ?? "keine Info", ); }, ); } else { return const Text("暂无数据"); } }, )
4. 修正_getAddressFromLatLng中的冗余类型转换
_getEvents()本身返回Future<List<Events>>,无需强制类型转换,直接赋值并触发状态更新:
// 替换原futureEvents = _getEvents() as Future<List<Events>>;的代码 setState(() { futureEvents = _getEvents(); });
内容的提问来源于stack exchange,提问作者HemmiFilmmusik
相关产品推荐
相关产品推荐

