Flutter查询附近ATM遇两类错误:空检查操作符与Firestore字段不存在
Flutter ATM定位应用报错排查
我正在开发一款收集附近ATM并展示的Flutter应用,流程是先获取用户当前位置,再用geoflutterfire查询Cloud Firestore中指定半径内的ATM。
目前遇到两个报错:
null check operator used on a null valueBad state: field does not exist within the DocumentSnapshotPlatform
注释掉代码里的GeoFirePoint point = geo.point(latitude: position1!.latitude, longitude: position1!.longitude);行后,空安全错误消失,但上述两个报错仍存在。
相关代码
import 'package:cloud_firestore_platform_interface/cloud_firestore_platform_interface.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'package:gab/pages/functions.dart'; import 'package:geocoding/geocoding.dart'; import 'package:geolocator/geolocator.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:geoflutterfire/geoflutterfire.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; final geo = Geoflutterfire(); final _firestore = FirebaseFirestore.instance; class Principal extends StatefulWidget { const Principal({Key? key}) : super(key: key); @override State<Principal> createState() => _PrincipalState(); } class _PrincipalState extends State<Principal> { // 获取位置 Position? position1; getCurrentLocation() async { Position position = await determinePosition(); setState(() { position1 = position; }); } Future<Position> determinePosition() async { LocationPermission permission; permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied){ permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied){ return Future.error("Location permissions are denied"); } } return await Geolocator.getCurrentPosition(); } @override Widget build(BuildContext context) { getCurrentLocation(); GeoFirePoint point = geo.point(latitude: position1!.latitude, longitude: position1!.longitude);// 此处报错:null check operator used on a null value! // GeoFirePoint point = geo.point(latitude: 4.0908151, longitude: 9.8037623); // _firestore.collection('atms').add({'bank': 'test', 'isFunctioning': true,'position':point.data}); var collectionReference = _firestore.collection('atms').where("isFunctioning", isEqualTo: true); double radius = 1000; String field = 'position'; Stream<List<DocumentSnapshot>> stream = geo .collection(collectionRef: collectionReference) .within(center: point, radius: radius, field: field, strictMode: true); stream.listen((List<DocumentSnapshot> documentList) { // print("Here"); // print(documentList[0].data()); }); return Scaffold( // 尝试过但无效 body: SafeArea( child: StreamBuilder<List<DocumentSnapshot>>( stream: stream, builder: (context, AsyncSnapshot<List<DocumentSnapshot>> snapshot){ if(!snapshot.hasData){ return Container( child: Text("No data"), ); } return Container( child: ListView.builder( itemCount: snapshot.data!.length, itemBuilder: ((context, index){ DocumentSnapshot data = snapshot.data![index]; GeoPoint documentLocation = data.get('location'); var distanceInMeters = Geolocator.distanceBetween(point.latitude, point.longitude, documentLocation.latitude, documentLocation.longitude); print('${distanceInMeters}'); return ListTile( title: Text('${data.get('bank')}'), subtitle: Text('${distanceInMeters}'), ); })), ); }, ), ), ); } }
报错与数据结构说明
- 空检查操作符错误:代码中对
position1使用空检查操作符时,position1仍为null。 - Firestore字段不存在错误:提示DocumentSnapshot中找不到指定字段。
- Cloud Firestore结构:
atms集合中的文档包含bank、isFunctioning、location字段,其中location为GeoPoint类型。
解决方案
1. 修复空检查错误
getCurrentLocation()是异步方法,在build方法中调用后position1不会立即赋值,导致空检查报错。修改方式:
- 将位置获取逻辑移至
initState,避免重复调用 - 使用前判断
position1是否为空,显示加载状态
修改后的代码片段:
@override void initState() { super.initState(); getCurrentLocation(); } @override Widget build(BuildContext context) { // 位置未获取到时显示加载 if (position1 == null) { return const Scaffold(body: Center(child: CircularProgressIndicator())); } GeoFirePoint point = geo.point(latitude: position1!.latitude, longitude: position1!.longitude); // 后续代码保持不变 }
2. 修复字段不存在错误
从Firestore结构可知,位置字段是location,但代码中存在两处不匹配:
- geoflutterfire查询时指定的
field为position - 未处理字段不存在的情况
修改步骤:
- 对齐查询字段:
// 改为Firestore中实际的位置字段名 String field = 'location'; - 安全获取字段,避免报错:
DocumentSnapshot data = snapshot.data![index]; // 使用defaultValue避免字段不存在时崩溃 GeoPoint? documentLocation = data.get('location', defaultValue: null); if (documentLocation == null) { return const ListTile(title: Text("无效ATM数据")); } var distanceInMeters = Geolocator.distanceBetween( point.latitude, point.longitude, documentLocation.latitude, documentLocation.longitude );
如果后续使用geoflutterfire存储ATM位置,需按其要求的格式存储:
// 用point.data生成geoflutterfire需要的结构 _firestore.collection('atms').add({ 'bank': 'test', 'isFunctioning': true, 'position': point.data });
内容的提问来源于stack exchange,提问作者Abdel
相关产品推荐
相关产品推荐

