You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter查询附近ATM遇两类错误:空检查操作符与Firestore字段不存在

Flutter ATM定位应用报错排查

我正在开发一款收集附近ATM并展示的Flutter应用,流程是先获取用户当前位置,再用geoflutterfire查询Cloud Firestore中指定半径内的ATM。

目前遇到两个报错:

  • null check operator used on a null value
  • Bad 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}'),

                 );
               })),
             );
           },
         ),
       ),

     );
  }
}

报错与数据结构说明

  1. 空检查操作符错误:代码中对position1使用空检查操作符时,position1仍为null。
  2. Firestore字段不存在错误:提示DocumentSnapshot中找不到指定字段。
  3. 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
  • 未处理字段不存在的情况

修改步骤:

  1. 对齐查询字段:
    // 改为Firestore中实际的位置字段名
    String field = 'location';
    
  2. 安全获取字段,避免报错:
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 02:24:55