上个月我接到一个适配任务:把一个跑在 Flutter 框架上的地图导航应用搬到鸿蒙设备上。原应用在 Android 上一直很稳,路径规划用的就是google_maps_directions这个 Flutter 三方库,填好起点终点,一次性拿回路线和分步导航数据。结果同步到鸿蒙真机上直接黑屏,连地图都出不来。排查了一圈,问题不在业务代码,而是这个库在鸿蒙设备上没有可用的底层依赖——GMS 不工作,Google Directions API 根本调不通。
这种"Android 好好的,鸿蒙一跑就废"的情况,现在越来越多。鸿蒙生态逐步成熟之后,很多 Flutter 项目都会遇到同一个选择题:要么砍功能,要么做适配。我选了后者。这篇博文就把我在鸿蒙化适配google_maps_directions时的完整思路、改造步骤和踩坑记录写下来,内容包括库的内部结构拆解、网络请求层替换、Polyline 编解码、坐标系偏移、导航状态机,以及当你连地图 API 都靠不住时,如何自研一套路径规划兜底。适合正在做 Flutter 鸿蒙化迁移、或者对 GIS 路径规划实现原理感兴趣的开发者参考。
1. 需求复盘:在鸿蒙 Flutter 应用里规划一条像样的路线有多麻烦
1.1 项目背景与原始选型
先交代一下这个项目的底子。这是一款面向物流场景的司机辅助 App,核心功能就是输入发货地和收货地,App 自动规划一条可驾驶路线,给出总里程、预计耗时,并在行驶过程中做转向提醒。技术上整体采用 Flutter 2.x 开发,地图底图用的是高德地图的 Flutter 插件,而路线规划这一层没有直接接高德的方向服务,而是接入了google_maps_directions。
当初这样选型的原因是研发团队里有人对 Google Directions API 的返回数据结构非常熟,而且这个库的模型封装得确实干净——DirectionsResult、Route、Leg、Step一套下来,直接就能映射到 UI 上,开发效率很高。在 Android 端,GMS 可用,这方案完全没毛病。
问题出在新一版需求上:项目要把运行环境覆盖到鸿蒙设备,尤其是 HarmonyOS NEXT 这种不兼容 Android APK 的系统形态。这时候google_maps_directions就变成了最大的风险点。我最初以为只是缺少 API Key 或者网络权限的问题,换一个 Key 就能解决,后来发现根子上不是这么回事。
1.2 鸿蒙设备上的第一步验证:现象与根因
拿到鸿蒙真机之后,我做的第一个动作是写一个最小复现工程,只保留一个按钮,点击后调用Directions().getDirections(request),看它到底报什么错。
| 运行环境 | 现象 | 根因判断 |
|---|---|---|
| Android(GMS 可用) | 正常返回路线 | Directions API 可访问 |
| 鸿蒙(无 GMS) | 请求抛出 network 异常 | 底层无法建立到 Google 服务的连接 |
| 鸿蒙模拟器 | 长时间无响应后超时 | 网络层被系统拦截 |
关键结论是:这不是 Flutter 层的 bug,而是整个 Google 服务链路在鸿蒙设备上不可用。google_maps_directions底层依赖 GMS,而鸿蒙设备从设计上就不带这套框架。所以在鸿蒙上做适配,思路不能是"修一修让它能跑",而是"把依赖 Google 的部分换成鸿蒙生态的地图能力"。
1.3 适配方案选型
我在调研阶段列了三个候选方案,简单做个对比:
- 继续用
google_maps_directions,底层通过某种方式代理请求——风险高,而且涉及外部代理设施,在真实物流场景下稳定性难以保证,放弃。 - 替换成鸿蒙 Map Kit 的方向服务能力,从 Flutter 层通过 MethodChannel 调用鸿蒙原生 SDK——这是最贴合鸿蒙生态的做法,决定采用。
- 自研路径规划算法,完全脱离第三方方向服务——作为兜底方案,在信号差、服务不可用的时候自动切换,后面我会专门讲。
最终我采用的是"2 为主、3 为兜底"的双通道策略。业务层不直接感知底层用了哪个服务商,只要拿到统一的路线模型就行。这样一来,无论是 Google、高德还是鸿蒙 Map Kit,都可以是路线的提供方,切换成本被压到最低。
2. 拆解google_maps_directions:一个方向服务客户端是如何工作的
2.1 先搞清这个库的请求链路
很多人用google_maps_directions只把它当黑盒:传origin、destination、travelMode,然后等结果。真到了要替换它的时候,第一步就是把黑盒拆开,搞清楚它到底做了什么。
这个库的核心就是一层薄封装。它做的事情可以拆成四步:
- 拼请求:把起点、终点、出行方式、是否避开高速等参数,组装成一个 GET 请求 URL,目标地址是 Google Directions API 的 endpoint。
- 发请求:走 Dart 层的
HttpClient发起网络请求。 - 解析响应:把返回的 JSON 映射成
DirectionsResult。 - 返回模型:业务层拿到
routes、legs、steps这些结构化的数据。
这四步里,第一步的参数组装是 ios-latin 无关的,第三步的 JSON 结构也和 Google 服务强相关。真正卡在鸿蒙上的是第二步——Google 的接口在鸿蒙设备上连不通。但它带来的启示是:如果我把"请求 URL 指向谁"这一层抽出来,改成调用鸿蒙 Map Kit,那么这个库的模型层就完全可以复用。
2.2 库封装的四个核心模块
从源码层面看,google_maps_directions的代码结构大概是这样的:
| 模块 | 职责 | 鸿蒙化时需要做什么 |
|---|---|---|
DirectionsRequest | 请求参数建模 | 保留,可以映射为鸿蒙方向服务的入参 |
Directions | 发起请求的服务类 | 替换内部实现,改用 Map Kit |
Location | 经纬度模型 | 保留 |
DirectionsResult / Route / Leg / Step | 响应模型 | 保留路线结构,新增 Map Kit 结果转换器 |
我最开始还想把模型也换掉,后来发现没必要。路线模型的核心字段是通用的:距离、时长、每条路段的起终点坐标、路段的多段线(Polyline)、导航动作( maneuver)。鸿蒙 Map Kit 返回的数据虽然字段名不同,但语义完全对得上。于是我的改造策略变成:只替换发请求那一层,把鸿蒙返回的 JSON 转换成DirectionsResult对应的模型。
2.3 为什么说这个库"好适配"
很多 Flutter 三方库都依赖 Google 服务,但适配难度差异很大。google_maps_directions算是好适配的一种,原因有三个:
- 它不依赖 Flutter 原生插件,纯 Dart 实现,不需要动 Android/iOS 原生代码。
- 它只做"请求—响应"两件事,没有历史包袱,没有复杂的本地数据库或缓存。
- 它的数据模型和行业通用模型基本一致,任何方向服务都能映射过来。
所以读者如果现在手里有类似的项目,不必一上来就推倒重写。先花两个小时把库的源码读一遍,判断它属于"纯网络封装型"还是"原生能力依赖型"。前者适合做服务替换,后者往往要重写整个插件层。
3. 鸿蒙化改造:从Google Directions API到Map Kit的迁移路径
3.1 接口层抽象
改造的第一步不是写鸿蒙代码,而是先定义一个不依赖任何服务商的接口。我在 Flutter 层加了一个抽象类:
abstract class DirectionsService { Future<DirectionsResult> getDirections({ required Location origin, required Location destination, TravelMode travelMode = TravelMode.driving, }); }然后创建两个实现:
GoogleDirectionsServiceImpl:保留原google_maps_directions逻辑,用于 Android(GMS 可用)等兼容环境。HarmonyDirectionsServiceImpl:通过 MethodChannel 调用鸿蒙端 Map Kit 方向服务。
业务层只依赖DirectionsService,具体用哪个实现由工厂方法根据平台决定。这样做的最大好处是:后面想再接高德、百度或者自研算法,都不用动 UI 层。
3.2 鸿蒙端封装方向服务
鸿蒙侧的对接用 ArkTS 写一个模块,暴露给 Flutter 调用。核心思路是:接收 Flutter 传过来的起点、终点和出行方式,调用 Map Kit 的方向服务 API,把结果转成 JSON 返回。
伪代码结构如下:
// HarmonyMapService.ets import { map } from '@kit.MapKit'; export function searchDirection(req: MapDirectionRequest): Promise<string> { return new Promise((resolve, reject) => { const directionSearch = new map.DirectionSearch(); directionSearch.search({ origin: { lat: req.originLat, lng: req.originLng }, destination: { lat: req.destLat, lng: req.destLng }, mode: req.travelMode }, (err, data) => { if (err) { reject(err); return; } // 将 data.routes 序列化为通用 JSON const routeJson = convertRoutes(data.routes); resolve(routeJson); }); }); }这里要注意一个工程细节:MethodChannel 传递大 JSON 会有性能损耗吗?我的实测是,单次路线规划返回的数据体量通常在 5KB-30KB 之间,对 MethodChannel 来说没有压力。真正需要担心的是高频调用场景(比如每 3 秒重新规划一次),这种情况建议走 EventChannel 或 Native 侧做节流。
3.3 一步步替换请求层
具体落地时,我在 Flutter 端做了这样几步:
第一步,在pubspec.yaml中把google_maps_directions从直接依赖改为可选依赖,新增对flutter/services.dart的调用。
第二步,实现HarmonyDirectionsServiceImpl,核心代码如下:
class HarmonyDirectionsServiceImpl implements DirectionsService { static const MethodChannel _channel = MethodChannel( 'com.example/harmony_map_directions' ); @override Future<DirectionsResult> getDirections({ required Location origin, required Location destination, TravelMode travelMode = TravelMode.driving, }) async { final json = await _channel.invokeMethod('searchDirection', { 'originLat': origin.latitude, 'originLng': origin.longitude, 'destLat': destination.latitude, 'destLng': destination.longitude, 'travelMode': travelMode.name, }); return DirectionsResult.fromJson(json); } }第三步,在鸿蒙工程里注册 MethodChannel 对应的 handler,并把转换后的 JSON 回传。
第四步,修改工厂类,在 HarmonyOS 上自动返回HarmonyDirectionsServiceImpl。这一步是关键,业务层代码一行都不用改。
整套改造做完之后,原本依赖 google_maps_directions 的页面,在鸿蒙上就可以正常获取路线了。有一个小坑提醒一下:鸿蒙 Map Kit 的方向服务需要先在华为开发者平台开通对应能力的权限,并配置 API Key,这个 Key 和地图底图的 Key 不一定通用,需要分别申请。
4. 踩坑实录:Polyline解码、坐标系偏移与请求层替换
4.1 编码Polyline的编解码算法
路线数据里最重要的一坨就是encoded polyline。Google Directions API 为了压缩体积,不会直接给你一串坐标数组,而是给一个被压得很密的 ASCII 字符串。鸿蒙 Map Kit 在这个字段上和 Google 的做法不太一样,有的接口直接给坐标点数组,有的会给出到"编码串"或者"会话类坐标串"。为了兼容,我把两套解析都做了。
先讲 Google 的编码原理,因为它能帮你理解为什么解码代码长这样:
- 原始经纬度值先乘 1e5 取整,用于转成整数运算。
- 相邻两个点之间的差值再编码,所以解码需要"当前值 = 上一值 + 差分量"。
- 差值左移一位,负数取反。
- 每 5 个 bit 一组,加上 0x20 后转成 ASCII 字符。
解码的 Dart 实现我贴在这里,这个算法在鸿蒙上也同样适用:
List<LatLng> decodePolyline(String encoded, {int precision = 5}) { final List<LatLng> points = []; int index = 0; int lat = 0; int lng = 0; while (index < encoded.length) { // 解码纬度差值 int result = 0; int shift = 0; int b; do { b = encoded.codeUnitAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); final int deltaLat = ((result & 1) != 0) ? ~(result >> 1) : (result >> 1); lat += deltaLat; // 解码经度差值 result = 0; shift = 0; do { b = encoded.codeUnitAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); final int deltaLng = ((result & 1) != 0) ? ~(result >> 1) : (result >> 1); lng += deltaLng; points.add(LatLng( lat / pow(10, precision).toDouble(), lng / pow(10, precision).toDouble(), )); } return points; }这里有个容易写错的地方:Google 默认精度是 5,也就是乘 1e5,但部分高精度路线的编码串会用 1e6。如果解码后发现路径在转弯处走形,先检查是不是精度写错了。鸿蒙某些接口的坐标点位较多,我也遇到过同一段路用 6 位精度编码的情况,最好在转换器里留一个精度参数,而不是写死。
4.2 WGS-84 与 GCJ-02 坐标系偏移
这是整个适配过程中最容易让人暴躁的一步。Google Directions API 返回的是 WGS-84 坐标,而鸿蒙 Map Kit 在国内使用的坐标体系是 GCJ-02(火星坐标)。如果你直接把 WGS-84 的坐标点往鸿蒙地图的 polyline overlay 上画,会发现整条路线整体偏移几十米,拐弯全在马路对面。
这个问题的本质是坐标系基准不同。WGS-84 是 GPS 设备直接输出的全球坐标系,而 GCJ-02 是为了国内地图政策要求,对经纬度做了一次非线性的加密偏移。所以不是"地图画错了",而是"坐标基准没转换"。
解决办法就是写一个转换层。鸿蒙端在把路线结果回传给 Flutter 之前,先判断坐标类型接口,如果是高德/华为自家服务返回的 GCJ-02,就直接透传;如果是外来的 WGS-84,先做一次 GCJ-02 加密转换。工程实现上我直接用了开源的坐标转换算法(WGS-84 转 GCJ-02),这里建议读者把它封装成一个工具类,不要散落在业务代码里。
另外提醒一点:定位模块拿到 GPS 原始数据是 WGS-84,如果你用的鸿蒙 Location Kit 自带坐标转换开关(有的版本支持直接返回 GCJ-02/SGC-02),优先打开,否则统一在你的转换层做一致处理。我最开始混用了两个定位源的坐标,导致导航箭头一会儿在路上一会儿在楼里。
4.3 请求层替换时的两个暗坑
超时与重试策略。Google Directions 的默认超时逻辑很短,换成鸿蒙 Map Kit 之后,接口在不同网络环境下的响应时间波动很大。我一开始沿用原来的 10 秒超时,结果在信号弱区域频繁超时。后来改成 15 秒超时,并带一次自动重试,稳定性明显提升。
航线规划请求字段缺失。有些业务场景需要"避开高速""避开拥堵",Google Directions API 有对应的请求参数。鸿蒙 Map Kit 早期版本不是所有参数都支持,我调接口时发现传了避开高速的参数,服务端直接忽略,返回了一条走高速的路线。这个只能做服务端能力映射,如果某个参数鸿蒙不支持,需要在产品层面做降级说明,而不是静默丢掉——否则司机按着路线走,会被导上一条他自己本来想避开的拥堵路。
5. 智能导航实战:实时定位、路线绘制与转向提醒
5.1 导航不是一个接口,是一个状态机
很多刚接触导航开发的同事以为,路径规划做完,导航就自动搞定了。实际上路径规划只是给了你一条"生产线",导航是让小车不停地在上面走,并且随时判断有没有走偏。
我把导航拆成五个状态:
| 状态 | 触发条件 | 处理动作 |
|---|---|---|
| 未开始 | 用户点击开始导航 | 获取定位,找到最近路线点,开始循环 |
| 沿路行驶 | 当前位置在路线附近 | 计算剩余距离/剩余时间,更新 UI |
| 转向提醒 | 距离下一个转向点小于阈值 | 语音+弹窗提醒,切换 Step |
| 偏航重规划 | 偏离路线超过 30 米 | 重新调用方向服务,拿到新路线 |
| 到达 | 距离终点小于 50 米 | 导航结束,弹到达页面 |
在鸿蒙设备上,这套状态机完全跑在 Flutter 层,只要底层DirectionsService能给出路线,和用的地图服务商无关。这再次印证了接口抽象的价值。
5.2 实时定位与路线匹配
实时定位用的是鸿蒙 Location Kit,通过 MethodChannel 把定位数据传给 Flutter。这里不要用 Flutter 自带的geolocator,因为它在鸿蒙上的底层适配还不完善,我测试时出现过定位频率不稳定、权限弹窗乱跳的问题。
定位频率要按驾驶场景调整。我设置为每秒刷新一次,如果发现位置点跳变过大(比如上一次和这一次距离超过 200 米),说明当前处于 GPS 信号不稳定区域(隧道、高架下),这个时候要开启"丢失定位"遮罩,避免导航箭头乱飞。
路线匹配的核心是投影算法。拿到当前坐标点后,遍历当前 Step 的 polyline 点,找到距离最近的两个点,把当前坐标投影到这两点构成的线段上。投影结果就是"匹配后位置",剩余距离就是从这个投影点沿路线到终点的路径长度之和。
5.3 转向提醒怎么算才不漏不重
转向提醒的触发条件非常容易做过头。最朴素的做法是"距离下个转向点小于 80 米就播报",但实际跑起来会发现三个问题:
- 在高速路上 80 米太短,语音说完还没变道就错过了。
- 在市区拥堵路段 80 米又太长,可能连播好几遍。
- 转向点密集的路口(连续两个右转),容易播报重叠。
我的做法是动态阈值:根据当前车速估算到达转向点的时间。当预计到达时间小于 12 秒且距离小于 300 米时触发一次提醒。同一个转向点只播报一次,如果用户在附近徘徊不掉头,则靠偏航重规划兜底。
转向动作的语义来自鸿蒙 Map Kit 的maneuver字段,和 Google 的maneuver字段语义存在差异。Google 那边是turn-left、turn-right、keep-left这类枚举,鸿蒙端有时候直接给中文说明如"左转""向右前方行驶"。我在转换层做了一层归一化,最终在 Flutter 端统一使用 Google 风格枚举,这样 UI 和语音层完全不用改。
5.4 路线绘制的性能优化
路线绘制看似简单,把 polyline 的点串成一个Polyline加到地图上就行。但真实路线的坐标点数可能高达上千个,尤其是一整段高速路线,直接把所有点一次画上去,低端鸿蒙手机会明显掉帧。
工程上我用的是抽稀(Douglas-Peucker)算法,在保证路线轮廓的前提下,把冗余点去掉。这个算法在 GIS 里是标配,思路很直接:找一条线的起点和终点连一条直线,然后找这条线上离线段最远的点,如果距离大于阈值就保留它,否则删掉中间的点,递归处理。我设置的阈值为 5 米,视觉上完全看不出变化,但点数量能减少 60%-80%。
6. 路径规划算法延伸:当API满足不了需求时自己写
6.1 从路网API到自由路径规划
前面几套方案虽然能覆盖大部分场景,但有一个隐患:路线规划完全依赖三方方向服务。如果 Map Kit 服务端故障、网络完全不可用,或者业务场景根本不是"城市道路"(比如园区内、工地上、停车场),司机端的导航就直接瘫痪了。
所以我在项目里做了一个兜底模块:一份轻量自研路径规划引擎,基于路网或栅格地图,不依赖任何在线 API。
自研引擎的价值不仅是"应急",还能覆盖很多特殊场景。比如物流园区内的车辆引导、最后一公里的泊车路径规划、AGV(自动导引车)的调度路线,这些场景用面向城市道路的 Directions API 根本拿不到路线,必须自己在栅格地图或拓扑图上面算。
6.2 A* 算法在栅格地图上的工程实现
我把自研引擎的定位精确为"基于栅格地图的 A* 寻路"。适合已经拿到园区地图、停车场地图,把地图切成网格(每个格子代表 1m×1m),障碍物格子标记为不可通行的情况。
A* 的核心逻辑不复杂:
openList存待探索节点,closedList存已探索节点。- 每个节点的代价
f = g + h,g是从起点走到当前节点的实际代价,h是从当前节点到终点的预估代价(用曼哈顿距离或欧氏距离)。 - 每次从
openList中取f最小的节点扩展,直到到达终点。
Dart 实现的关键伪代码:
class AStarPathFinder { List<Offset> findPath(List<List<bool>> grid, Offset start, Offset goal) { final openSet = PriorityQueue<AStarNode>((a, b) => a.f.compareTo(b.f)); final cameFrom = <String, AStarNode>{}; final gScore = <String, double>{}; final fScore = <String, double>{}; openSet.add(AStarNode(start, 0, heuristic(start, goal))); while (openSet.isNotEmpty) { final current = openSet.removeFirst(); final currentKey = nodeKey(current.pos); if (current.pos == goal) { return reconstructPath(cameFrom, current); } for (final next in getNeighbors(current.pos, grid)) { final tentativeG = current.g + 1.0; final nextKey = nodeKey(next); if (tentativeG < (gScore[nextKey] ?? double.infinity)) { cameFrom[nextKey] = current; gScore[nextKey] = tentativeG; fScore[nextKey] = tentativeG + heuristic(next, goal); openSet.add(AStarNode(next, tentativeG, fScore[nextKey]!)); } } } return const []; } }这里容易踩的坑是启发函数的选择。在允许斜向移动的栅格地图上,如果用曼哈顿距离作为启发,A* 会倾向于走很多直角折线,路线难看。我换成了欧氏距离,路线更直接,代价是搜索节点增多一点点。场景是停车引导的话,我更推荐欧氏距离加拐角惩罚——对 AGV 和小车来说,少转弯比少走几米更有价值。
6.3 AGV、无人机和三维路径规划的延展思考
做完栅格 A* 之后,我对这套能力做了一个横向评估,发现它可以延伸到项目后续的多个方向:
AGV 多车调度。A* 只是单机寻路,现实中园区里可能同时有 5-10 台 AGV。多车共用一个栅格地图时,必然出现路径冲突,需要引入时间维度的预留机制——每台车规划路线时,把"未来 N 秒要占用的格子"登记到时间窗表里,其他车规划时避开这些时空格子。这个思路和交通路线中"实时路况避堵"是同构的。
无人机路径规划。无人机在城市里飞行,路线不能只看 2D 网格,要扩展成 3D 空间,障碍物也不只是墙壁,还包括高度限制区域、禁飞区。常用算法会从 A* 换到 RRT(快速扩展随机树),因为 3D 空间网格化后搜索空间爆炸,A* 的代价太高。RRT 的做法是在三维空间里随机撒点,不断往目标方向生长树,直到找到一条无碰撞路径。这个算法随机性强,给出的路径往往不是最优,但"买"到了巨大的速度优势。
泊车路径规划。停车场和道路最大的区别在于——停车场是一大块自由空间,里面散布着柱子和其他车,路线不遵循固定道路线。这就更贴近"自由空间规划",A* 可以用,但更常用的是混合 A*(Hybrid A*),它会把车辆的运动学约束(最小转弯半径)考虑进去,保证规划出来的路径小车真的能开出来。
这些延伸方向其实都建立在地图建模之上:先把世界抽象成"可通行/不可通行"的格子或拓扑节点,然后才能在图上搜索。这个建模能力,是比"调一个路况 API"更值钱的东西,也是 GIS 三种基本数据模型——矢量数据、栅格数据、属性表——中栅格模型最常见的应用场景。
7. 真机验证、性能对比与后续扩展方向
7.1 真机测试矩阵与结论
整个改造完成后,我在三台设备上做了回归测试,测试项覆盖了路线规划正确性、导航连续性、坐标系偏差、极端场景路径。
| 设备 | 系统版本 | 路线规划耗时 | 编解码耗时 | 结论 |
|---|---|---|---|---|
| Mate 60 | HarmonyOS 4.0 | 约 1.2s | 约 30ms | 通过 |
| P60 | HarmonyOS 4.0 | 约 1.4s | 约 30ms | 通过 |
| 鸿蒙平板 | HarmonyOS 3.1 | 约 1.8s | 约 40ms | 路线偏差点稍大,总体可用 |
和 Android 上 Google Directions 的耗时(约 0.8s-1.0s)相比,鸿蒙 Map Kit 的耗时略高,但差异主要在网络握手和鉴权环节,体感不明显。真正需要注意的是定位持续开启带来的耗电问题,锁屏导航 1 小时大约耗电 8%-10%,这在低端机上还会放大。
7.2 性能优化与稳定性增强
我在最终版本里追加了三个稳定性措施,如果你也在做类似的模拟开发,可以直接抄作业:
路线缓存。同一个起点终点的规划结果在 5 分钟内直接复用,避免用户反复点选同一路线时重复请求。缓存的 key 是起点经纬度_终点经纬度_出行方式,注意浮点精度,建议四舍五入到小数点后 4 位再拼接。
降级策略。自研 A* 引擎平时不启用,只在检测到 Map Kit 连续两次请求失败时开启。降级后路线精度从"道路级"降为"网格级",但至少用户还能看到一个可走的路径方向。
日志与监控。所有调用方向服务的耗时、状态码、失败原因统一上报。这个非常重要,因为鸿蒙 Map Kit 的不同版本、不同机型上行为差异比 Android 大得多,没有日志你很难定位具体是哪个设备的问题。
7.3 后续扩展方向
适配完成后,这套架构给我最大的启发是:不要把路线规划能力绑定在某一家服务商上。现在项目里的DirectionsService接口可以随时接入高德、百度、华为或者自己的 A* 引擎,这让我在谈新的合作场景时非常从容。
如果你也在做 Flutter 鸿蒙化适配,我的建议是:先不要急着写鸿蒙原生的复杂逻辑,先从接口抽象开始,把一个最小的"能拿到路线"的链路跑通,再逐步叠加坐标转换、导航状态机、自研兜底。这条路我走了一遍,整体是顺的,最大的成本反而不是代码本身,而是理解鸿蒙生态与 Google 生态之间的"坐标差异"和"语义差异"。把这些理解透,你的 App 就真正具备跨生态导航能力了。