调用Future函数后值仍为Null?Flutter地理定位数据处理问题
异步获取地理距离并排序列表的问题
我用geolocator插件获取地理定位数据,想把拿到的距离值存入列表用来排序,但添加数据时出现了值为Null的错误,还有调用add()方法时的Null接收器错误。我知道这是Future变量和普通变量处理方式不一样导致的,想请教怎么把Future变量转换成普通变量?相关代码和错误如下:
主代码(创建Widget列表,需按距离排序)
return ListView( // 此部分正常工作,用于获取Firebase文档 children: snapshot.data.docs.map((document) { GeoPoint tempVar = document['location']; // 此部分无法正常工作 List<int> widgetIds; Future<double> listValue = getDistance(tempVar); listValue.then((double future) { if(listValue != null){ var newV = myList(listValue.round()); print('$newV'); } if(listValue == null){} }); // 此部分正常工作,用于生成自定义Widget return canTemplate(document, context,); }).toList(), );
getDistance()函数
Future <double> getDistance(GeoPoint loc) async { Position position = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high); double lat1 = position.latitude; double long1 = position.longitude; double lat2 = loc.latitude; double long2 = loc.longitude; double distanceInMeters = await Geolocator().distanceBetween(lat1, long1, lat2, long2); double feet = distanceInMeters * 3.28084; return feet; }
myList函数
Future <List> myList(int data) async{ List widgetIds; widgetIds.add(data); return widgetIds; }
遇到的错误
/flutter ( 5177): Closure: (int) => Future<List> from Function 'myList': static. E/flutter ( 5177): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: NoSuchMethodError: The method 'add' was called on null. E/flutter ( 5177): Receiver: null E/flutter ( 5177): Tried calling: add(1046046)
可以看到目标数值能获取到,但还是被判定为Null。另外尝试用await调用时,把var newV = myList(listValue.round());改成var newV = await myList(listValue.round());,系统提示“await只能用于Future,不能用于double”。
解决方法
1. 先修复myList的空指针问题
myList里的widgetIds没有初始化,直接调用add()肯定报错,改成:
// 没必要做成Future,同步函数即可 List<int> myList(int data) { List<int> widgetIds = []; // 初始化空列表 widgetIds.add(data); return widgetIds; }
2. 正确获取Future的实际值
在then回调里,你错误地用了listValue.round(),但listValue是Future对象,不是实际的double值,应该用回调参数里的真实值:
listValue.then((double actualValue) { if(actualValue != null){ var newV = myList(actualValue.round()); print('$newV'); } });
3. 实现按距离排序的正确思路
你当前的写法是在遍历文档时异步获取距离,没法直接排序(因为ListView已经提前生成)。正确流程应该是:
- 先批量获取所有Firebase文档
- 一次性计算所有文档的距离,把文档和距离绑定成对象
- 按距离排序后再生成ListView
示例代码:
// 定义类存储文档和对应距离 class DocumentWithDistance { final DocumentSnapshot doc; final double distance; DocumentWithDistance(this.doc, this.distance); } // 封装排序逻辑的异步方法 Future<List<DocumentWithDistance>> _getSortedDocuments() async { // 获取当前用户位置 Position position = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high); double userLat = position.latitude; double userLng = position.longitude; // 获取Firebase所有文档 QuerySnapshot snapshot = await FirebaseFirestore.instance.collection('你的集合名').get(); // 遍历文档计算距离,存入列表 List<DocumentWithDistance> docList = []; for(var doc in snapshot.docs) { GeoPoint loc = doc['location']; double distanceInMeters = Geolocator.distanceBetween(userLat, userLng, loc.latitude, loc.longitude); double feet = distanceInMeters * 3.28084; docList.add(DocumentWithDistance(doc, feet)); } // 按距离从小到大排序 docList.sort((a, b) => a.distance.compareTo(b.distance)); return docList; } // 用FutureBuilder构建排序后的ListView @override Widget build(BuildContext context) { return FutureBuilder<List<DocumentWithDistance>>( future: _getSortedDocuments(), builder: (context, snapshot) { if(snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } if(snapshot.hasError) { return Center(child: Text('加载出错')); } return ListView( children: snapshot.data.map((item) { return canTemplate(item.doc, context); }).toList(), ); }, ); }
4. 关于await错误的解释
你之前用await myList(listValue.round())报错,是因为listValue是Futureround()是在Future对象上调用方法,不是真实的double值。正确写法需要先await获取实际值:
// 必须在async函数内使用 double actualValue = await listValue; var newV = myList(actualValue.round());
内容的提问来源于stack exchange,提问作者Grace Thomas
相关产品推荐
相关产品推荐

