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

Flutter调用HERE Maps REST API获取位置数据时出错求助

问题排查与修复

核心错误点

  1. 实体类字段不匹配:Item类中定义的localityType为必填字段,但HERE Maps API返回的JSON里没有这个字段,解析时会直接抛出空值异常。
  2. 根数据解析逻辑错误:API返回的是单个包含items数组的对象,但原代码把它当成了HereRestLocation对象数组来解析,导致类型转换失败。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 22:19:50