Flutter调用HERE Maps REST API获取位置数据时出错求助
问题排查与修复
核心错误点
- 实体类字段不匹配:
Item类中定义的localityType为必填字段,但HERE Maps API返回的JSON里没有这个字段,解析时会直接抛出空值异常。 - 根数据解析逻辑错误:API返回的是单个包含
items数组的对象,但原代码把它当成了HereRestLocation对象数组来解析,导致类型转换失败。 - UI层数据传递错误:
MapList组件接收的是List<HereRestLocation>,但实际需要展示的是HereRestLocation内部的items列表(即List<Item>)。
修复后的完整代码
import 'dart:async'; import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; HereRestLocation hereRestLocationFromJson(String str) => HereRestLocation.fromJson(json.decode(str)); String hereRestLocationToJson(HereRestLocation data) => json.encode(data.toJson()); class HereRestLocation { List<Item> items; HereRestLocation({ required this.items, }); factory HereRestLocation.fromJson(Map<String, dynamic> json) => HereRestLocation( items: List<Item>.from(json["items"].map((x) => Item.fromJson(x))), ); Map<String, dynamic> toJson() => { "items": List<dynamic>.from(items.map((x) => x.toJson())), }; } // 修正数据解析逻辑:返回单个HereRestLocation对象 HereRestLocation parseHereRestLocation(String responseBody) { final parsed = jsonDecode(responseBody); return HereRestLocation.fromJson(parsed); } Future<HereRestLocation> fetchHereRestLocation(http.Client client) async { final response = await client.get(Uri.parse('https://discover.search.hereapi.com/v1/discover?at=40.7307999,-73.9973085&limit=2&q=Liberty&apiKey=**My API key here**')); // 确保请求成功再解析 if (response.statusCode == 200) { return compute(parseHereRestLocation, response.body); } else { throw Exception('请求失败,状态码:${response.statusCode}'); } } class Item { String title; String id; String language; String resultType; ItemAddress address; Highlights? highlights; // API返回的部分Item可能没有highlights,改为可选参数 Item({ required this.title, required this.id, required this.language, required this.resultType, required this.address, this.highlights, }); factory Item.fromJson(Map<String, dynamic> json) => Item( title: json["title"], id: json["id"], language: json["language"], resultType: json["resultType"], address: ItemAddress.fromJson(json["address"]), highlights: json["highlights"] != null ? Highlights.fromJson(json["highlights"]) : null, ); Map<String, dynamic> toJson() => { "title": title, "id": id, "language": language, "resultType": resultType, "address": address.toJson(), "highlights": highlights?.toJson(), }; } class ItemAddress { String label; String countryCode; String countryName; String state; String county; String city; String? stateCode; String? district; String? street; String? postalCode; String? houseNumber; ItemAddress({ required this.label, required this.countryCode, required this.countryName, required this.state, required this.county, required this.city, this.stateCode, this.district, this.street, this.postalCode, this.houseNumber, }); factory ItemAddress.fromJson(Map<String, dynamic> json) => ItemAddress( label: json["label"], countryCode: json["countryCode"], countryName: json["countryName"], state: json["state"], county: json["county"], city: json["city"], stateCode: json["stateCode"], district: json["district"], street: json["street"], postalCode: json["postalCode"], houseNumber: json["houseNumber"], ); Map<String, dynamic> toJson() => { "label": label, "countryCode": countryCode, "countryName": countryName, "state": state, "county": county, "city": city, "stateCode": stateCode, "district": district, "street": street, "postalCode": postalCode, "houseNumber": houseNumber, }; } class Highlights { List<Title> title; HighlightsAddress address; Highlights({ required this.title, required this.address, }); factory Highlights.fromJson(Map<String, dynamic> json) => Highlights( title: List<Title>.from(json["title"].map((x) => Title.fromJson(x))), address: HighlightsAddress.fromJson(json["address"]), ); Map<String, dynamic> toJson() => { "title": List<dynamic>.from(title.map((x) => x.toJson())), "address": address.toJson(), }; } class HighlightsAddress { List<Title> label; List<Title> city; HighlightsAddress({ required this.label, required this.city, }); factory HighlightsAddress.fromJson(Map<String, dynamic> json) => HighlightsAddress( label: List<Title>.from(json["label"].map((x) => Title.fromJson(x))), city: List<Title>.from(json["city"].map((x) => Title.fromJson(x))), ); Map<String, dynamic> toJson() => { "label": List<dynamic>.from(label.map((x) => x.toJson())), "city": List<dynamic>.from(city.map((x) => x.toJson())), }; } class Title { int start; int end; Title({ required this.start, required this.end, }); factory Title.fromJson(Map<String, dynamic> json) => Title( start: json["start"], end: json["end"], ); Map<String, dynamic> toJson() => { "start": start, "end": end, }; } void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { const appTitle = 'Isolate Demo'; return const MaterialApp( title: appTitle, home: MyHomePage(title: appTitle), ); } } class MyHomePage extends StatelessWidget { const MyHomePage({super.key, required this.title}); final String title; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(title), ), body: FutureBuilder<HereRestLocation>( future: fetchHereRestLocation(http.Client()), builder: (context, snapshot) { if (snapshot.hasError) { return Center( child: Text('错误:${snapshot.error}'), // 显示具体错误信息,方便排查 ); } else if (snapshot.hasData) { return MapList(items: snapshot.data!.items); } else { return const Center( child: CircularProgressIndicator(), ); } }, ), ); } } class MapList extends StatelessWidget { const MapList({super.key, required this.items}); final List<Item> items; @override Widget build(BuildContext context) { return GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, ), itemCount: items.length, itemBuilder: (context, index) { return Text(items[index].title); }, ); } }
额外优化建议
- 在请求失败时抛出具体状态码异常,方便排查API密钥或请求参数问题。
- 将
highlights字段改为可选参数(因为部分返回结果可能没有该字段),避免解析失败。 - 在错误提示中显示具体错误信息,而不是泛泛的“An error has occurred!”,有助于快速定位问题。
内容的提问来源于stack exchange,提问作者Kavishika_Kahandawala
相关产品推荐
相关产品推荐

