尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Flutter+OpenHarmony实战:井盖地图巡检App的完成率统计与性能优化

Flutter+OpenHarmony实战:井盖地图巡检App的完成率统计与性能优化 1. 为什么是Flutter OpenHarmony这套组合解决的实际问题井盖管理这事听着不起眼真正落过地的人才知道有多头疼。我最早做的是传统原生Android巡检App一套业务逻辑写完换个平台几乎等于重写。后来项目要求适配OpenHarmony设备我当时第一反应是完了又得养一套代码。后来调研了一圈Flutter的鸿蒙支持情况发现这套组合已经不是“能不能用”的阶段而是“怎么用得更顺”的阶段了。Flutter自身跨端渲染的机制决定了它天然适合这种多端碎片化场景OpenHarmony作为新兴系统最缺的就是生态应用两者结合恰好踩在需求点上。城市井盖地图App的核心业务并不复杂在地图上展示井盖点位、记录巡检状态、统计完成率。难的地方在于你要在一套代码里同时兼顾地图渲染性能、定位精度、业务数据的组织方式以及最容易被忽视的——完成率计算口径。如果只做原型随便糊一个进度条就行但真正给市政维护人员用完成率背后必须有一套严谨的统计逻辑否则上报的数据根本没法用。这篇实战记录适合几种人看一是准备在OpenHarmony上做Flutter应用但还在观望的开发者二是做地图类巡检应用但不知道怎么组织业务数据的同行三是对“完成率”这类进度统计有复杂需求、想找参考实现的人。我尽量把从环境搭建到功能落地的完整过程写清楚包括踩过的坑和绕过的弯每一处关键选择都会解释背后的理由方便你在自己的项目里做取舍。先说结论Flutter在OpenHarmony上跑业务级应用目前已经具备生产条件但有三件事必须在动手前想清楚——工具链版本怎么锁、地图SDK怎么接、状态管理怎么选。这三件事没定好后面全是返工。2. 开发环境与工程初始化OpenHarmony上跑通Flutter的那些坎2.1 工具链版本锁定是第一优先级我一开始犯的错就是“能装上就用最新的”结果光环境就折腾了两天。Flutter对OpenHarmony的适配是跟着官方仓库的节奏走的这跟普通Flutter版本管理完全是两回事——你不能只关心Flutter SDK版本还得关心OpenHarmony SDK的API Level、ArkTS编译器的兼容范围、以及Flutter引擎和鸿蒙侧的bridge实现是否匹配。实际可用的组合是这样的Flutter SDK选择3.16.x以上的版本我最终锁定的是3.19.x系列OpenHarmony SDK选择API 10或API 11。这里有个关键点——Flutter官方版本发布节奏和OpenHarmony的适配进度是有时间差的你新装的Flutter版本如果超出了当前适配补丁的覆盖范围编译时各种奇奇怪怪的报错就会冒出来。查问题的时候第一反应不应该是去搜“flutter build报错解决方案”而是先确认版本组合是不是官方测试过的组合。下载Flutter SDK这件事建议直接从官方镜像仓库拉取不要用第三方打包的版本因为你根本不知道里面改了什么东西。我见过有人用国内某博客下载的Flutter SDK跑普通Android项目没问题一跑OpenHarmony直接崩了。OpenHarmony SDK则是用DevEco Studio自带的SDK Manager统一管理不要手动配置环境变量去指否则版本动不动就被覆盖。2.2 Windows环境下配置OpenHarmony交叉编译链在Windows上开发OpenHarmony应用最痛苦的是交叉编译环境。Flutter工程调用鸿蒙侧能力的时候需要本机的Node.js环境、hdc命令行工具、以及OpenHarmony SDK中的toolchains。我建议把完整环境按固定顺序来配先装DevEco Studio它会带一套完整的OpenHarmony SDK和hdc工具再装Flutter SDK并切换到适配分支配置flutter config里的ohos-sdk路径指向DevEco安装目录下的sdk文件夹最后配置环境变量把hdc和node的路径加到PATH里。很多人第一步就栽了先装的Flutter后装的DevEco然后flutter doctor死活检测不到OpenHarmony环境。原因就是flutter config里存的ohos-sdk路径还是旧值你装了新SDK之后它并不会自动更新。处理方式很简单重新执行一次flutter config --ohos-sdk并指定新路径就行。编译上有个坑我印象深刻就是首次构建项目时Gradle需要下载大量依赖。由于国内网络状况这个过程可能等很久甚至直接超时。这里就涉及一个很多人不知道的小技巧OpenHarmony工程构建时会用到ohpm作为包管理器你需要提前配置ohpm的镜像源否则第三方鸿蒙依赖几乎下不动。配置方法是在ohpmrc文件里增加registry字段指向可用的镜像地址这一步配置好之后构建速度提升是肉眼可见的。2.3 用命令行创建工程而不是IDE向导创建工程我强烈建议用命令行flutter create --platforms ohos 项目名。用IDE向导创建出来的工程会附带一堆默认模板代码对Flutter OpenHarmony这种组合来说模板代码意味着你需要手动删掉很多不兼容的依赖。创建完成之后进目录看一眼你会发现结构和纯Flutter工程有些差异ohos目录下是一个完整的鸿蒙工程有entry模块和配置文件顶层是Flutter标准的lib目录业务代码都写这里依赖配置文件里会多出一些flutter_ohos相关的包。这里要特别注意不要在lib目录下放任何与平台相关的原生代码比如直接引用鸿蒙的Ability或者UIAbility的能力。Flutter侧的架构原则是所有平台能力通过MethodChannel走桥接lib目录保持纯Dart状态。这个原则贯穿了整个开发过程后面讲到地图SDK接入时会再次体现出来。3. 井盖地图的核心数据模型怎么设计才能支撑完成率统计3.1 井盖点位的数据结构设计井盖地图App冷启动的第一件事就是加载点位数据。数据模型的设计直接决定了后续所有功能的开发效率。我在原型阶段用的模型非常简单我只建了一个CatchBasin类字段包括id、经度、纬度、地址描述、状态未巡检/已巡检/异常、巡检时间。这个模型应付Demo足够但到了真实场景完全不够用——因为完成率统计需要按维度切分单纯的点位状态存不了这些维度信息后面你需要再回去改表、改模型、改接口动作非常大。后来调整为分层模型核心类是class ManholeCover { final String id; final String code; // 井盖编号用于线下核对 final double latitude; final double longitude; final String district; // 行政区 final String street; // 所属街道用于分组统计 final ManholeType type; // 类型雨水/污水/电力/通信 final ManholeStatus status; // 巡检状态 final DateTime? lastInspectionTime; final double weight; // 权重用于加权完成率 }这里有个关键的决策加了district和street字段这样完成率可以按任意层级聚合。而weight字段是后来新增的因为实际业务里不同井盖的巡检价值不一样主干道上的井盖和背街小巷的井盖不能按同一个权重算完成率。3.2 完成率的数据口径与聚合逻辑完成率看起来就是一个百分比但真正做起来最容易出问题的是聚合逻辑。我遇到过的情况是地图上显示总完成率35%但按街道筛完之后某条街道的完成率变成了95%两条信息放在同一个报告里业务方直接质疑数据有问题。排查后发现是统计口径的问题总完成率的分子分母是全球范围内的井盖街道维度的分子分母是该街道范围内的井盖两个百分比之间本身没有可比性。如果你把“全量完成率”定位成“各街道完成率的平均值”那算法就又不一样了。最终我采用了按层级递归聚合的实现先从最细粒度的街道开始算完成率再根据街道井盖数量对街道完成率做加权平均得到区域完成率再对区域做加权平均得到全市总完成率。每一层都保留点位明细方便下钻排查。class CompletionStats { final int totalCount; final int inspectedCount; final int abnormalCount; final double completionRate; // inspectedCount / totalCount * 100 factory CompletionStats.fromPoints(ListManholeCover points) { final total points.length; final inspected points.where((p) p.status ManholeStatus.inspected).length; final abnormal points.where((p) p.status ManholeStatus.abnormal).length; return CompletionStats( totalCount: total, inspectedCount: inspected, abnormalCount: abnormal, completionRate: total 0 ? 0 : inspected / total * 100, ); } }聚合计算放在内存里做不依赖后端接口实时算。原因很简单井盖点位数据量在万级以内全量加载到内存完全没压力实时计算反而节省了网络请求数据刷新更快。如果你管理的是几十万甚至百万级别的井盖点位那这套方案就需要改成后端聚合前端只展示结果的形式架构逻辑会不同但界面层的设计可以完全复用。3.3 本地持久化与技术选型地图App在信号不好的地方比如地下管道附近也得能查看历史记录和统计结果。第一次我建议引入本地数据库但后来反复测试后发现shared_preferences加JSON序列化的组合对这点数据量来说已经绰绰有余——把点位列表序列化成JSON文件存本地启动时反序列化加载速度毫秒级。真正需要本地数据库的场景是点位数据量大、需要复杂的条件查询比如按时间范围状态街道组合筛选、以及离线状态下需要写入新巡检记录。我评估之后发现这三个场景在前期版本里都不会高频出现——查询逻辑可以通过内存里的数组二次筛选实现新记录的写入频率一天也就几十条。所以最终选择点位基础数据json文件存assets目录随App发布巡检状态变更shared_preferences保存每次变更的增量记录完成率统计结果纯计算不持久化每次展示时实时算。这套方案实现简单数据一致性好也方便调试。等项目规模真正上来了再迁移到数据库迁移路径也是平滑的。4. 地图渲染与井盖点位标注在Flutter里接地图SDK的完整思路4.1 地图SDK选型比对为什么没用默认的Google地图地图是这类App的核心组件技术选型直接决定体验上限。Flutter生态里最成熟的地图插件基本绑定Google Maps或苹果地图但OpenHarmony设备上没有Google服务这条路直接封死。剩下的选择是高德地图、百度地图、或者自己用Canvas画一张简化地图。三方地图SDK在OpenHarmony上的适配情况是这样的SDK鸿蒙适配Flutter插件离线地图路况信息定制自由度高德有主动适配需自写桥接层支持支持中百度有SDK版本需自写桥接层支持支持中自绘Canvas无需适配内置天然支持不支持高我最终选了高德地图加自写桥接层的方案——原因在实际测试里很明确高德的OpenHarmony SDK虽然还处于快速迭代期但基础的地图显示、Marker标注、定位功能已经能稳定工作而且它家对Flutter社区的支持明显更积极网上能找到的踩坑资料也更多。自绘地图的定制性虽高但在真实坐标转换、缩放手势、道路数据这些层面自己做成本太高得不偿失。4.2 在Flutter侧封装地图组件在Flutter侧地图组件我用PlatformView嵌入原生地图SDK。这里的核心代码结构是class MapView extends StatefulWidget { final ListManholeCover points; final ValueChangedManholeCover? onMarkerTap; override StateMapView createState() _MapViewState(); } class _MapViewState extends StateMapView { final _channel const MethodChannel(app/map); override Widget build(BuildContext context) { return PlatformViewLink( viewType: mapView, onCreate: (context, params) _createAndroidView(params), ); } // 通过MethodChannel与原生侧通信 Futurevoid _addMarkers(ListManholeCover points) async { final data points.map((p) { id: p.id, lat: p.latitude, lng: p.longitude, status: p.status.index, }).toList(); await _channel.invokeMethod(addMarkers, data); } }这里踩过一个很坑的问题PlatformView和Flutter手势之间的事件冲突。地图需要响应拖动、缩放、点击Flutter列表需要响应滑动两者嵌套在一起时经常出现“地图拖动把页面也带走了”或“页面滑动把地图也带偏了”的问题。解决办法是在原生侧设置地图的手势优先级并在Flutter侧给地图组件外的区域做好hitTestBehavior控制。Marker的渲染方式一般有两种原生Marker和自定义Overlay。原生Marker在拖动地图时性能好但样式定制受限自定义Overlay自由度高、能实现酷炫的动画效果但数量多了会掉帧。我实际测试发现井盖点位数量在1000个以内时原生Marker的点击响应最稳定超过1000个时即便不卡顿用户点击的准确性也会受影响。所以前期产品设计上就做了点位聚合——缩放级别低时显示聚合数字放大后显示单个Marker。这个交互逻辑比优化渲染性能更重要。4.3 定位能力接入的细节问题定位权限是这类App绕不开的环节。OpenHarmony的权限模型跟Android类似需要在module.json5里声明ohos.permission.LOCATION权限同时还有一层用户动态授权弹窗需要处理。我遇到过这样一个问题地图SDK定位成功回调已经触发了但Flutter侧拿到的位置一直是初值。排查后发现是原生侧把位置数据封装成了自定义对象而我在Flutter侧用MapString, dynamic去接收类型对不上导致解析失败。这种问题在MethodChannel通信中非常典型——原生侧和Flutter侧的数据类型映射必须严格对齐。// 原生侧返回的定位数据需要显式转换为JSON可序列化的结构 var result { latitude: location.latitude, longitude: location.longitude, accuracy: location.accuracy, timestamp: DateTime.now().millisecondsSinceEpoch, };数据的传递路径是原生侧把对象转成MapMethodChannel把Map序列化成JSONFlutter侧再用as Mapdynamic, dynamic接住。中间任何一个环节出现类型不匹配就是简简单单的一个NullPointerException没有任何其他提示。5. 完成率功能的完整落地过程从数据采集到可视化呈现5.1 巡检状态流转的设计完成率的代码实现其实很简单难点在于“什么算完成”这件事要想清楚。最初我把状态定义成二元组已巡检/未巡检。但真正用起来发现巡检人员在现场最常见的操作是发现问题比如井盖破损、下沉、污水外溢如果只有“已巡检”这个概念这些问题数据就无法沉淀下来。所以我把状态流转设计为四态enum ManholeStatus { pending, // 未巡检 inspected, // 已巡检-正常 abnormal, // 已巡检-异常 repaired, // 异常已修复 }完成率的计算口径是inspected abnormal repaired 都算“已覆盖”只有pending算未覆盖。异常状态的存在让完成率能区分“覆盖了但有问题”和“没覆盖”两种截然不同的情况——这个区分对业务方来说很重要因为一个100%完成但发现100处异常的片区和一个100%完成但没有异常记录的片区管理含义完全不一样。5.2 前端进度统计的中央状态管理完成率数据需要在地图页、列表页、统计页三个地方共用而且任何一处修改状态都要实时同步到其他页面。这就是典型的中央状态管理需求。我在这个项目里用的是Riverpod没有用Bloc原因是Riverpod的声明式和组合式API在这种中等复杂度项目里心智负担最小。状态结构做成可观察的异步数据流final inspectionRepositoryProvider ProviderInspectionRepository((ref) { return InspectionRepository(); }); final statsProvider FutureProviderMapString, CompletionStats((ref) async { final repo ref.watch(inspectionRepositoryProvider); final points await repo.loadAllPoints(); return calculateStatsGroupByStreet(points); });这里有一个很实用的经验状态变更后不要全局刷新地图Marker只增量更新被修改的那个Marker的颜色和状态图标。全局刷新在数据量小的时候看不出问题但一旦地图上同时存在几百个Marker全局刷新会肉眼可见地闪一下。增量更新的实现方式是在Repository层维护一个变更通知接口Marker更新走独立通道。5.3 完成率的可视化呈现不只是一种进度条完成率可视化我做了三层每一层解决不同的问题第一层是全局总览卡片一个环形进度条展示全市总的完成率。这一层解决的是“干了多少”的问题用圆形进度条百分比文本就够了不需要过度设计。第二层是分街道进度列表每条街一个横向进度条带数字。这一层解决的是“哪里干得好哪里干得差”的问题可以方便地排出优先级。我把完成率低于60%的街道用红色进度条标出60%-80%用橙色80%以上用绿色视觉上非常直观。第三层是地图上的热力标记按区域的完成率在地图上给不同区域覆盖半透明色块。这一层解决的是“地理位置分布”的问题——管理人员不用去翻Excel就能一眼看出西南片区进度落后了。这三层可视化组件之间完全解耦共用同一个statsProvider数据源。加一个新的可视化维度只需要创建新的widget来订阅同一个provider即可。环形进度条的核心实现环形进度条用CustomPaint实现避免引入额外的图表库。核心是靠Canvas画圆弧class CompletionRing extends StatelessWidget { final double rate; // 0-100 final String label; override Widget build(BuildContext context) { return CustomPaint( painter: _RingPainter(rate: rate), child: SizedBox( width: 120, height: 120, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(${rate.toStringAsFixed(1)}%, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)), Text(label, style: const TextStyle(fontSize: 12, color: Colors.grey)), ], ), ), ), ); } } class _RingPainter extends CustomPainter { final double rate; override void paint(Canvas canvas, Size size) { final center Offset(size.width / 2, size.height / 2); final radius size.width / 2 - 10; final backgroundPaint Paint() ..style PaintingStyle.stroke ..strokeWidth 10 ..color Colors.grey.shade300; final progressPaint Paint() ..style PaintingStyle.stroke ..strokeWidth 10 ..strokeCap StrokeCap.round ..color rate 80 ? Colors.green : (rate 60 ? Colors.orange : Colors.red); canvas.drawCircle(center, radius, backgroundPaint); canvas.drawArc( Rect.fromCircle(center: center, radius: radius), -Math.pi / 2, // 起始角度12点钟方向 2 * Math.pi * rate / 100, // 扫过角度按比例计算 false, progressPaint, ); } override bool shouldRepaint(covariant _RingPainter oldDelegate) oldDelegate.rate ! rate; }实现本身不复杂但有两个细节值得提一是drawArc的起始角度需要从-90度开始画否则进度会从3点钟方向出发很丑二是当完成率是100%时圆弧的终点和起点会完全重合如果strokeCap是round类型会看到一个小小的圆点需要特殊处理一下。5.4 数据的实时同步与刷新策略巡检人员在外场作业时网络状况不稳定App需要支持离线记录巡检结果等有网了再同步到服务端。这个离线同步机制我单独写了一个SyncManagerclass SyncManager { final QueueSyncTask _pendingTasks Queue(); Futurevoid submit(String recordId, ManholeStatus status) async { _pendingTasks.add(SyncTask(recordId: recordId, status: status)); await _persistQueue(); // 先存本地 if (await _isNetworkAvailable()) { await _flushQueue(); } } Futurevoid _flushQueue() async { while (_pendingTasks.isNotEmpty) { final task _pendingTasks.first; final success await _sendToServer(task); if (success) { _pendingTasks.removeFirst(); } else { break; // 失败就停等下次触发 } } await _persistQueue(); } }这个机制本身不复杂但让我意识到一个隐藏需求完成率的计算必须同时考虑本地未同步的数据。如果巡检人员早上离线巡检了5个井盖这5条记录已经让本地完成率变了但服务端数据还没更新此时如果直接展示服务端的统计结果就会看到完成率在一天里不停“倒退”。解决办法是统计层做合并——先统计服务端数据再把本地pending队列里的增量记录叠加进去计算逻辑保持透明。6. OpenHarmony上的性能问题与画面渲染异常排查记6.1 画面渲染异常一个印象深刻的真机bug开发过程中遇到的最让人头疼的问题是地图页在OpenHarmony真机上偶发画面渲染异常。现象是地图拖到某个区域后地图瓦片变成全白要知道井盖点位还在上面绘制着完全重叠视觉上一片混乱。查这个问题花了我将近半天。一开始以为是地图SDK的问题后来发现原生地图App没问题那就是Flutter渲染和原生地图之间的事。仔细查看日志后发现问题出在Flutter引擎和OpenHarmony图形栈之间的同步上——当Flutter页面使用PlatformView嵌入原生地图时如果Flutter侧有频繁的动画比如Marker的弹跳动画混编层容易出现纹理同步失败。这类渲染异常在OpenHarmony上比Android上更容易出现原因是OpenHarmony的图形渲染栈跟Android不完全一样纹理共享和互操作层的实现在细节上还有一些边界case没有处理干净。我当时的处理方式是避免在PlatformView上层做频繁的动画把Marker的弹跳动画改成静态态切换状态颜色变化的轻量交互渲染率明显下降。第二个工作是给地图组件设置hybridComposition模式这个模式在Flutter 3.19对OpenHarmony的支持已经比较成熟了。开启方式是在AndroidManifest类似的配置里设置对应参数或者通过PlatformViewLink的创建参数指定。实测下来hybridComposition把白屏概率从“偶发”降到了“几乎没遇到”但会带来一些轻微的帧率损耗需要在性能和稳定性之间做取舍。6.2 列表滚动性能优化缓存是关键井盖列表页的滚动流畅度直接决定用户对这个App的第一印象。原始版本直接使用ListView.Builder逐条构建卡片在低端设备上滚动时明显掉帧因为每条卡片里有两层Container装饰、一个图标、三段文本每帧Build的负担不小。优化思路很常规但非常有效把卡片拆分复用自动缓存。class ManholeListItem extends StatelessWidget { final ManholeCover item; override Widget build(BuildContext context) { return Container( margin: EdgeInsets.symmetric(horizontal: 12, vertical: 4), padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 4)], ), child: Row( children: [ _StatusIcon(status: item.status), SizedBox(width: 12), Expanded(child: _ItemInfo(item: item)), _StatusTag(status: item.status), ], ), ); } }在跑性能分析之后发现瓶颈反而不是卡片构建而是字体渲染和阴影效果的开销。最终调整去掉大面积boxShadow改用细边框线列表项关闭saveOffset特性对图片类资源进行了本地缩略图预生成不在运行时做缩放。还有一个影响体验的小细节当用户在地图上点击一个Marker跳转到列表页并滚动到对应项时会有短暂的白屏闪烁。原因是列表页每次打开都重新build了所有数据后来给页面加了AutomaticKeepAliveClientMixin做页面保活切换Tab时不再重新加载数据体验提升明显。6.3 Gradle构建与包体积控制OpenHarmony版本的Flutter App在打包环节和普通Flutter有细微差别。普通Android的Gradle配置可以直接套用但OpenHarmony构建时有一个多出来的产物hap包。hap是鸿蒙系统的安装包格式和Android的apk格式逻辑类似但打包方式和签名机制完全不同。我在打包阶段遇到的一个典型问题是release包安装后能启动但地图页面显示空白debug包一切正常。后来定位到是高德地图SDK的包名绑定问题——release包和debug包用了不同的签名导致地图SDK鉴权失败。解决办法是把release包和debug包都配置成同一位的keystore并在高德后台把两个包名的SHA1都加到白名单里。包体积方面原始release包大小是37MB。Flutter的引擎so文件占了大约15MB地图SDK占了8MB其余是资源文件和Dart代码。小幅压缩可以通过--split-debug-info和--obfuscate来做但实测对最终体积影响不大真正的优势是Flutter的Dart代码几乎不占包体积多个平台复用一份代码维护成本降低了很多倍。7. 多端适配与上架验证从开发机到真机的完整测试记录7.1 不同OpenHarmony设备的兼容性问题OpenHarmony设备的碎片化程度比Android更严重不同厂商的硬件配置差异极大。我手头的测试设备覆盖了开发板RK3568、中端手机、以及一款带触控屏的行业终端设备。测试过程中最明显的差异是屏幕分辨率和内存大小的适配问题。1024x600的分辨率下地图控件占满全屏后底部操作栏会挤压到一个比较尴尬的高度1920x1080下一切正常而在智能屏设备上由于系统字体缩放机制不同列表项的文字被放大后出现换行错乱。解决办法是布局层面全面改成自适应用MediaQuery.of(context).size做布局比例计算而不是写死像素值。内存方面2GB内存的设备上同时开着地图和列表页返回时偶尔出现白屏或应用被杀的情况。处理方式是监听AppLifecycleState页面不可见时及时释放地图资源。这个“资源释放”逻辑要特别小心因为它可能走不到你的析构函数就被系统强杀了所以持久化操作一定要在状态切换的瞬间就做掉不能依赖deactivate回调。7.2 上架OpenHarmony应用市场的检查项如果你准备上架鸿蒙应用市场有几个预检项比代码本身更磨人隐私政策链接应用涉及定位权限必须提供隐私政策页面地址权限声明一致性代码里申请的每个权限都要在隐私政策文案里有对应说明截屏和功能录屏不同屏幕分辨率的设备各需要一组签名证书和profile文件需要用DevEco Studio生成正式签名证书配置到工程里。我有个小建议功能录屏时把“完成率变化过程”录进去比如手动标记一个井盖为已巡检后总览卡片上的百分比同步变化这种动态展示比静态截图更有说服力。审核人员看到这个流程对App的核心价值理解会快很多。7.3 版本迭代的核心维护策略迭代过程中容易忽略的是OpenHarmony的API兼容性。鸿蒙的系统API更新比Android激进有些API在新版本直接标记废弃或删除了你在编译时可能没有问题因为编译依赖的是你本地SDK版本但装到新版系统的设备上就容易崩溃。我的经验是用最低API Level的SDK来编译不追求新特性保持基础功能在旧设备上不闪退等用户量上来后再做针对新系统特性的适配。加上Flutter引擎本身的升级节奏我给自己定了一个维护周期每三个月检查一次Flutter和OpenHarmony主版本是否有重大变更评估是否升级业务功能上新不过分依赖平台特性的情况就不动工具链把有限的精力放在业务迭代上。8. 当前版本的上限与后续演进方向从功能完整性看这个版本的井盖地图App已经覆盖了核心业务闭环查看地图点位、巡检打卡、状态流转、完成率统计、离线同步。在真机上跑了两周稳定性和性能表现都达到了可交付状态。目前的架构保留了几个清晰的可扩展点一是数据层接口目前全部是本地mock接入真实后端只需要替换Repository层实现。接口设计已经预留了按街道、按时间段的查询参数后端只要按协议返回JSON前端逻辑无需改动。二是地图组件层做了封装如果需要替换成百度地图或者自绘引擎只需要改动MapView的内部实现业务层感知不到变化。三是完成率算法目前是内存聚合如果数据量增长到十万级可以把聚合逻辑下沉到后端前端保留同样的展示接口。下一阶段我想做的功能是巡检路线的智能推荐。基于当前的点位分布和历史巡检记录可以算出一条最优巡检路径减少巡检人员的无效移动距离。这个功能的计算模型可以复用现有数据但需要引入路径规划SDK。届时地图层的抽象能力优势就能体现出来——替换和扩展都很方便。最后分享一个我在整个项目里最深刻的体会做这种业务型的App技术选型上最怕的就是“为了新而新”。Flutter OpenHarmony这个组合放在两年前多少有点尝鲜性质但到现在它的价值已经不是“能不能跑”的问题而是“怎么跑得稳、怎么跑得省”的问题。如果你也正在评估这个方向建议上手先做一个最小可用的原型把地图渲染和状态管理跑通再迭代业务功能。这条路我走过了节奏对了效率非常高。
返回列表