Flutter中geocoding的locationFromAddress抛出空值异常排查
在Chrome中通过flutter run运行Flutter Web应用时,调用geocoding包的locationFromAddress方法抛出"Unexpected null value"错误,但直接HTTP调用谷歌地理编码API正常,已启用Geocoding API并在index.html配置了API密钥,需检查以下内容:
需要检查的项
geocoding包Web端配置完整性
确认index.html中谷歌Maps JavaScript API的加载脚本包含geocoding库,格式如下:<script src="https://maps.googleapis.com/maps/api/js?key=你的API密钥&libraries=places,geocoding"></script>同时检查API密钥无拼写错误、多余空格,确保注入位置正确(在Flutter的
main.dart.js加载之前)。版本兼容性问题
当前使用geocoding 3.0.0+Flutter 3.24.4,可能存在版本适配问题。尝试升级geocoding到最新稳定版,或降级至与Flutter 3.x匹配的版本(如geocoding 2.1.1),再测试是否复现错误。空值解析逻辑问题
geocoding包在Web端解析谷歌API响应时,可能对某些字段使用了强制非空断言(!),若API返回的字段缺失就会触发空值错误。对比直接HTTP调用的返回数据,检查是否存在字段在包的解析逻辑中被假设为非空,但实际返回为null;也可查看geocoding包的Web端源码,确认解析逻辑是否存在潜在空值风险。API密钥的域名限制
尽管直接HTTP调用正常,但需确认Google Cloud控制台中API密钥的HTTP引用者限制是否包含本地开发域名(如http://localhost:XXXX,XXXX为你的Flutter Web端口),避免因域名白名单限制导致geocoding包调用失败。
相关代码参考
问题代码片段
/// Searches for an address and moves the map Future<void> _searchAddress() async { String query = _searchController.text.trim(); if (query.isEmpty) { debugPrint("Search query is empty."); return; } try { debugPrint("Calling locationFromAddress for: $query"); List<Location> locations = await locationFromAddress(query); debugPrint("locationFromAddress response: $locations"); if (locations.isEmpty) { throw Exception("No locations found for the query."); } } catch (e) { debugPrint("locationFromAddress error: $e"); // Outputs "locationFromAddress error: Unexpected null value." } } }
完整页面代码
import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:geocoding/geocoding.dart'; class FindAddressScreen extends StatefulWidget { const FindAddressScreen({super.key}); @override State<FindAddressScreen> createState() => _FindAddressScreenState(); } class _FindAddressScreenState extends State<FindAddressScreen> { LatLng _selectedLocation = LatLng(37.7749, -122.4194); final TextEditingController _searchController = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Select Address'), actions: [ TextButton( onPressed: () { Navigator.pop(context, _selectedLocation); }, child: Text("Conferma"), ) ], ), body: Stack( children: [ GoogleMap( initialCameraPosition: CameraPosition( target: _selectedLocation, zoom: 14, ), onMapCreated: (controller) { }, onLongPress: _updateLocation, markers: { Marker( markerId: MarkerId("selected-location"), position: _selectedLocation, ) }, ), Positioned( top: 10, left: 10, right: 10, child: Card( child: Row( children: [ Expanded( child: TextField( controller: _searchController, decoration: InputDecoration( hintText: "Search address", contentPadding: EdgeInsets.symmetric( vertical: 10, horizontal: 15, ), border: InputBorder.none, ), ), ), IconButton( icon: Icon(Icons.search), onPressed: _searchAddress, ), ], ), ), ) ], ), ); } /// Updates the selected location Future<void> _updateLocation(LatLng position) async { setState(() { _selectedLocation = position; }); } /// Searches for an address and moves the map Future<void> _searchAddress() async { String query = _searchController.text.trim(); if (query.isEmpty) { debugPrint("Search query is empty."); return; } try { debugPrint("Calling locationFromAddress for: $query"); List<Location> locations = await locationFromAddress(query); debugPrint("locationFromAddress response: $locations"); if (locations.isEmpty) { throw Exception("No locations found for the query."); } } catch (e) { debugPrint("locationFromAddress error: $e"); } } }
可用的HTTP调用替代实现
import 'package:http/http.dart' as http; import 'dart:convert'; // ... Future<void> _searchAddress() async { String query = _searchController.text.trim(); if (query.isEmpty) { debugPrint("Search query is empty."); return; } final apiKey = "YOUR_API_KEY"; // 替换为你的API密钥 final url = Uri.parse( "https://maps.googleapis.com/maps/api/geocode/json?address=${Uri.encodeComponent(query)}&key=$apiKey"); try { final response = await http.get(url); if (response.statusCode == 200) { final data = json.decode(response.body); if (data['results'] != null && data['results'].isNotEmpty) { final location = data['results'][0]['geometry']['location']; LatLng target = LatLng(location['lat'], location['lng']); _moveCamera(target); _updateLocation(target); debugPrint("Address found: $location"); } else { throw Exception("No results found."); } } else { throw Exception("Failed to fetch geocoding data."); } } catch (e) { debugPrint("Google Maps search error: $e"); } } }
环境信息
- 依赖版本:google_maps_flutter 2.10.0、geocoding 3.0.0、http 1.2.2
- 运行环境:Chrome 131.0.6778.86、Flutter 3.24.4、Dart 3.5.4、VS Code 1.95.3、macOS 15.0(MacBook Air M1 8GB)
内容的提问来源于stack exchange,提问作者Filippo

