ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Flutter鸿蒙跨平台开发实战:从家庭账单到全流程踩坑复盘

Flutter鸿蒙跨平台开发实战:从家庭账单到全流程踩坑复盘 家里记账这件事我和几个朋友折腾了挺久。一开始是微信群接龙后来换到备忘录和Excel最终还是决定自己写个应用。关键点是家里人用的手机系统不统一有鸿蒙、有安卓、也有iOS如果每个平台都单独开发一套这个小项目根本扛不住。所以很自然地想到了Flutter这条路——这也是目前跨平台开发里能在鸿蒙上跑出原生体验、又不用大规模改业务逻辑的方案之一。正好那段时间团队在研究鸿蒙适配我也趁机把Flutter和鸿蒙这套链路完整走了一遍从SDK版本匹配、DevEco Studio接入、共享数据结构设计到插件兼容性排查和上架阶段的问题处理整个过程踩了不少文档里根本不会写的坑。这篇文章就围绕家庭账单共享本这个具体项目把Flutter框架跨平台鸿蒙开发从选型到落地的完整过程拆给你看。适合两类人一类是想把已有Flutter项目移植到鸿蒙上跑的开发者另一类是正在纠结用什么方案做鸿蒙应用、又不想完全用ArkTS重写的产品侧技术同学。1. 为什么家庭账单这个项目我最后选定了 Flutter 鸿蒙路线1.1 需求背景全家人的账单不该只躺在一个人的手机里先说说产品需求。所谓家庭账单共享本核心场景就是一家人可能分居两地也可能在一个屋檐下共同维护一份账目谁交了水电费、谁买了菜、孩子补习班花了多少、房贷卡是哪天扣的。每个人都往里面记一笔每个人都能看到完整的账本月末还能快速知道这个月钱花在了哪里。如果只是一个人记账用任何记账App都能解决。一旦变成多人共享、实时同步事情就复杂起来了需要一套家庭成员和家庭组的体系至少要能邀请成员、区分成员角色账单数据要有归属概念每一笔记录是谁记的、最后谁改的得能追踪不同角色看到的统计口径可能不一样比如我只想看餐饮老婆可能想看全部跨平台是最基础的需求——家里长辈用鸿蒙手机很常见年轻人则往往是安卓和iOS混用。原来的临时方案是用腾讯文档共享表格但这东西在手机上编辑体验太差而且没有分类、没有图表、没有提醒。大家开会讨论后决定做个真App。既然要真做第一个问题选什么技术栈。1.2 三条技术路线的对比当时摆在桌面上的方案主要有三个ArkTS原生、uni-app、Flutter。ArkTS原生HarmonyOS的ArkUI声明式开发体验最好的是系统交互和生态适配但问题也很直接只能覆盖鸿蒙一个平台。项目里安卓和iOS的用户还占一半为一套代码额外维护三个技术栈成本完全失控。而且团队里原本没有熟悉ArkTS的人新人学习成本也不低。uni-app是另一个常见选择。语法接近Vue上手快对微信小程序也能复用。但我们在项目里发现几个现实问题uni-app在鸿蒙上跑主要是通过编译成ArkTS包或者使用内置webview渲染前者对复杂交互的还原度有损耗后者在性能上不太理想。加上我们需要比较自由的Canvas绘制和动画曲线uni-app这种偏模板化的方案后期定制会很吃力。Flutter当时在鸿蒙上已经有了实际的运行路径。Flutter用的是自绘引擎UI不依赖系统组件这意味着同一套渲染逻辑搬到鸿蒙设备上理论上是成立的只要引擎层能跑通。官方和社区也都在推进Flutter的鸿蒙支持已经有不少团队把它用在生产环境了。还有一个对比项是Tauri最近频繁出现在热门讨论里。Tauri的优势是体积小、基于WebView渲染、Rust后端但它在鸿蒙上的成熟度主要取决于鸿蒙WebView的能力开放程度。我们项目里有些本地数据计算和图表绘制走WebView方案总感觉多了一层隔膜最终没有选它。1.3 最终选择和分析最终拍板用Flutter还有个很实际的原因代码资产可以继承。Flutter这套东西不只服务鸿蒙安卓、iOS、甚至Windows桌面版都能跑。如果家庭账单共享本以后想出一个PC端给父母在电脑上看账本Flutter也可以直接出一版桌面应用。而ArkTS和uni-app在这方面的扩展性要么被平台锁死要么需要重新做一套UI层。当然选Flutter不等于完全放弃鸿蒙原生能力。鸿蒙特有的服务卡片、系统级分享、近距离通信这些功能Flutter侧可以通过编写平台通道Platform Channel来调用ArkTS层的能力。这个思路跟Flutter在安卓和iOS上做原生扩展完全一致团队不用学一整套新框架只需要掌握鸿蒙侧的最小API集就行。我在项目里的角色是技术选型和主程序开发。下面所有内容都是基于这次真实开发过程总结出来的你可以把它当作一份技术复盘也可以直接按流程复现。2. 项目架构与共享数据模型设计2.1 功能拆解家庭账单共享本第一版规划了四个核心模块账本首页按月份展示账单流水支持滑动切换月份顶部卡片汇总当月收入、支出、结余。记账页新增/编辑一笔账单字段包括金额、分类、账户、成员、备注、日期、是否有附件。家庭成员页创建家庭、邀请成员、设置权限管理员/普通成员、查看成员记账统计。报表页按分类汇总月度趋势图成员支出排行榜。这里面最容易踩坑的不是UI而是数据模型。如果模型设计不合理后面做共享、统计、权限都会受牵连。2.2 数据模型我设计的数据表分四张家庭表、成员表、账单表、分类表。其中账单表和成员表是最核心的。账单表字段大致是这样的class Bill { final String id; final String familyId; // 所属家庭组 final String categoryId; // 分类ID final double amount; final int type; // 0支出 1收入 final String payerId; // 花费/收入的实际承担者 final String recorderId; // 记账人 final DateTime billedAt; // 账单发生时间 final String note; final DateTime createdAt; final DateTime updatedAt; }这里特别说明几个设计决策payerId和recorderId分开。刚开始我图省事只留了一个userId字段后来发现问题很大妈妈帮爸爸记一笔账的时候这笔钱到底算谁的如果统计成员支出必须区分记在谁头上和谁用手机记的。这个字段一分开报表就随便写了。type用int而不是枚举字符串。因为这个数据要落到本地SQLite同时同步到远端int在序列化、比较、建索引上都比字符串省事。硬删除改为软删除。共享场景下A删了一笔账B的设备上不能直接消失而应该标记为已删除等同步协议确认后再真正清理。我在模型里加了一个deletedAt字段。这个设计前期看起来多余但后来和撤销删除功能一起用体验提升非常明显。成员表的设计影响权限和展示class FamilyMember { final String userId; final String familyId; final int role; // 0管理员 1普通成员 final String nickname; final String avatarUrl; final bool isOwner; // 是否是家庭创建者 }权限规则也不复杂管理员可以编辑任意账单、删除成员、修改家庭资料普通成员只能编辑自己记录的账单但可以查看全部账单。这套规则直接决定后端接口要不要做鉴权也决定本地端要隐藏哪些编辑入口。2.3 状态管理与共享同步策略状态管理这块项目里用了BLoC/Cubit。现在Flutter社区聊BLoC和Cubit的讨论度很高它俩的区别简单说BLoC把事件和状态拆开适合复杂交互Cubit直接调用方法触发状态变化写起来快很多。我们的账单列表、成员列表这类场景用Cubit完全够没必要上全套BLoC来增加模板代码。共享同步策略上我选了本地优先 云端消息队列的方案。本地优先的意思是所有读写先操作SQLite再异步同步到云端。这样即使断网家里人也能正常记一笔等网络恢复后自动推送。同步时的冲突解决规则定的是updatedAt时间戳靠后覆盖但deletedAt优先于普通更新。规则很朴素但对于家庭账本这种低并发写入场景足够用。真正要扛高并发的记账工具需求其实不强反而能离线记一笔的诉求特别高。3. 环境搭建把 Flutter 和鸿蒙构建链串起来3.1 SDK选型的第一个坑在鸿蒙上跑Flutter不是随便装个官方的Flutter SDK就行。目前实际可用的路径是使用支持鸿蒙适配的Flutter SDK通常来源于OpenHarmony开源社区的flutter_flutter分支或者华为开发者官网推荐的适配版本配合DevEco Studio完成鸿蒙侧的工程配置。我一开始直接用官方stable分支的Flutter SDK配了DevEco Studio结果新建的鸿蒙工程根本识别不了Flutter模块构建日志在前两百行就开始报错。后来才意识到问题Flutter对鸿蒙的支持是通过OpenHarmony的组件化适配实现的普通发行版SDK默认没有启动鸿蒙相关的构建工具链。这个环节最容易踩到的提示就是The current configured Flutter SDK is not known to be fully supported. Please ...遇到这个提示别慌。它只是说SDK版本和当前工程的Flutter鸿蒙适配版本存在匹配关系问题。通常的处理方式是把flutter_flutter分支拉下来切换到项目指定的tag然后重新跑flutter doctor。鸿蒙SDK、DevEco Studio、Flutter SDK三者之间有一个矩阵对应关系建议你到项目文档里去找那份表格直接照抄能省一个下午。3.2 从下载到能跑通示例工程完整的环境搭建流程大概是这样的安装DevEco Studio因为Flutter鸿蒙侧最终还是通过它编译产物。注意DevEco Studio版本要满足Flutter适配分支的要求版本太老或太新都可能出现工具链不匹配。下载flutter_flutter适配分支SDK解压后通过flutter命令配置好环境变量并且用flutter config把SDK目录指向它。用DevEco Studio打开Flutter工程中的鸿蒙壳工程目录一般是项目的ohos目录。先在DevEco Studio里用自带的Previewer跑一遍页面预览如果没有真机也可以先走本地模拟器。让DevEco Studio和Flutter工具链分别执行一次构建确认两侧的签名、模块引用都正常。真实开发中还经常出现一个情况Flutter侧能编译但DevEco Studio侧认不到Flutter模块或者反过来鸿蒙工程能编译但整个App一启动就是白屏。这类问题大多数是Flutter模块的产物路径配置错了去检查鸿蒙壳工程里的依赖引用和构建输出目录一般能找到原因。3.3 没有真机和模拟器也能调试的方案开发过程中很多人会问手里没有鸿蒙手机也装不了本地模拟器能不能调试我试过几种方案按优先级排序DevEco Studio里的Previewer适合看页面布局和基础交互启动快不用设备。本地模拟器Local Emulator适合跑完整App流程。如果能装上大部分日常开发够用了。但模拟器对Flutter的GPU渲染支持有限制跑复杂动画会出现掉帧这是正常的不要因为这个去优化代码。云测试平台华为开发者平台提供了一定的远程真机调试能力适合做兼容性验证比如验证低内存机型下的表现。免费的额度有限但拿来验证一个项目完全够用。实际过程中我的组合拳是Previewer解决80%的UI问题本地模拟器解决流程交互问题最后找一台别人淘汰下来的二手鸿蒙手机做真机验证。真机验证特别要注意的一点是鸿蒙真机默认会对Hilog日志级别做裁剪有些Flutter侧的debugPrint输出在设备上可能看不到得通过过滤进程号或者临时把日志级别调低才能拿到完整日志。4. 家庭账单共享本核心功能实现4.1 账单列表页账单列表页是整个App的门面。用Cubit管理月份切换和列表加载状态核心结构是class BillListCubit extends CubitBillListState { BillListCubit(this._repository) : super(BillListState.initial()); Futurevoid loadMonth({required DateTime month}) async { emit(state.copyWith(loading: true)); final bills await _repository.byMonth(month); emit(state.copyWith(loading: false, bills: bills)); } }页面用BlocBuilder监听状态列表项展示账单分类图标、金额、备注和记账人。这里要注意一个性能细节ListView在Flutter里默认是懒加载的但如果账单项里有复杂的图像比如小票照片建议在外层先显示缩略图等滑动停止后再加载原图。我第一版直接加载原图在低端鸿蒙机型上滑动掉帧很明显后来改为缩略图方案才解决。还有一个UI细节很值得提列表项的Dismissible滑动手势。Flutter默认的滑动删除在安卓上很顺手但在鸿蒙上系统级返回手势和列表横滑有时会冲突导致滑删操作变得迟钝。解决办法是给Dismissible设置direction只在DismissDirection.endToStart有效同时调整滑动灵敏度参数。4.2 新增账单表单新增账单的页面是Flutter的原生表单没有用三方包。字段不多但交互细节多class ExpenseForm extends StatefulWidget { // ... } class _ExpenseFormState extends StateExpenseForm { final _formKey GlobalKeyFormState(); double _amount 0; String _category 餐饮; int _type 0; DateTime _date DateTime.now(); String? _note; // ... }金额输入框我用了TextInputFormatter限制只能输入两位小数并且禁止负数。日期选择用showDatePicker分类用Wrap排列的ChoiceChip。表单提交前先校验然后写入本地库。这里有一个真实坑鸿蒙上的输入法对数字键盘的支持在某些版本上有bug用TextInputType.numberWithOptions(decimal: true)弹出的键盘可能没有小数点按键。当时搜索flutter tts和flutter socketexception这类问题的时候我顺手看到不少鸿蒙输入法的反馈最后在设置里发现应该用inputFormatters配合正则白名单过滤非数字和小数点字符而不是依赖系统键盘的行为。这个改动很小但直接决定了记账功能在鸿蒙手机上的可用性。表单存储成功后用SnackBar提示用户然后立即刷新列表页同时把这条改动加入待同步队列。4.3 家庭共享与成员管理家庭共享是项目区别于普通记账App的关键。整个逻辑分三步创建家庭用户首次打开App后提示创建家庭组生成一个家庭ID和邀请码。邀请成员成员输入邀请码并确认加入。实际发邀请码可以走短信、微信分享链接或者近场设备碰一碰但第一版为了省时间直接做了复制邀请码功能。成员与权限管理管理员可以查看成员列表、移出成员、修改成员昵称。Flutter侧的成员管理页面比较标准重点在权限控制。比如普通成员点击删除按钮时前端要判断bool canDeleteBill(Bill bill, FamilyMember currentUser) { if (currentUser.role 0) return true; return bill.recorderId currentUser.userId; }这个判断规则简单但很重要。因为如果前端不做限制任何人删除同步过来的账单都会在协作时引发到底谁删的的矛盾。云端再强制做一次鉴权双重保险。4.4 统计报表统计报表我用了图表库和本地聚合查询两种方式。图表库选的是fl_chart它能画柱状图和折线图。但这里有一个很现实的兼容性问题第三方图表库如果依赖了比较底层的绘制能力在鸿蒙上的表现并不完全一致。因为Flutter的自绘引擎在鸿蒙上做了适配绝大多数基本绘制API都可用但一部分需要GPU加速的复杂Shader效果会有兼容问题。我的应对策略是柱状图、折线图这些基础图表直接用fl_chart绘制效果稳定圆环图这种就自己用CustomPaint画代码量也不大还能完全控制渲染行为。报表数据用一条SQL完成SELECT categoryId, SUM(amount) FROM bills WHERE familyId ? AND type 0 AND billedAt BETWEEN ? AND ? GROUP BY categoryId;这个查询在数据量达到几千条时依然很快不需要额外做缓存。家庭成员支出排行稍微复杂一点按payerId分组就行但要注意一个坑及时同一笔账转给多个人分摊的情况。第一版没做分摊后来发现奶粉钱是AA的这种需求在家庭账本里居然特别高频。所以我在账单模型里预留了一个split字段存JSON格式的成员比例报表统计时分摊金额而不是整笔金额。这个改动看似不复杂但涉及数据模型和所有统计SQL的修改务必在项目早期就考虑进去不然上线后想补这个功能要重写一遍报表逻辑。5. 鸿蒙实测期踩过的坑5.1 插件兼容性是最现实的问题Flutter社区几千个pub包能用在鸿蒙上的数量要打一个折扣。尤其是一些直接叫系统服务的插件比如地理定位、蓝牙、指纹识别必须在鸿蒙侧找到对应的原生实现才能工作。我最开始想用一个Star数很高的keep in mind工具库来做本地存储加密和日志归档结果发现它在鸿蒙上直接编译不过因为底层调用了安卓特有的API。后来换成了鸿蒙侧自带的Preferences和上下文文件管理能力通过平台通道桥接回来效果反而更可控。这里我的建议是接手Flutter鸿蒙项目之前先花半天把依赖列表过一遍标记出哪些插件是纯Dart实现一般跨平台没问题哪些依赖平台通道。纯Dart插件可以直接信任平台通道插件就要逐个检查有没有鸿蒙适配版本。常见的做法是有的插件虽然没有鸿蒙官方适配版但有社区维护的开源鸿蒙版本package名带hms或ohos前缀可以替换使用。5.2 请求报错 2300056 的排查链路项目里有一个很典型的网络问题同一个后端接口安卓真机请求正常一到鸿蒙真机就报2300056错误。这个数字我一开始完全没头绪。网上搜了半天发现这其实是鸿蒙网络框架里的一个通用错误码跟HTTP状态码不一样它对应的是系统网络栈的某个异常状态。排查链路我用的是排除法先确认是不是权限问题。鸿蒙应用要在module.json5里申请ohos.permission.INTERNET如果是HarmonyOS NEXT还得单独确认。检查之后权限是对的。确认是不是明文HTTP限制。安卓9以上默认不允许HTTP明文传输鸿蒙也有类似的机制但配置位置在network_security_config里。项目测试环境的接口恰好是HTTP的根本没有配置放行这就有可能触发2300056。最终定位是测试环境证书问题。鸿蒙的网络栈校验了自签名证书而安卓那边由于平时开了接受所有证书的开关所以没暴露。我把测试环境证书加到信任列表后问题消失。这个坑的具体报错码可能因为系统版本不同而变化但排查思路是一样的永远先怀疑安全和权限配置再怀疑代码逻辑。网络请求在安卓正常、鸿蒙异常大概率不是Flutter代码的问题而是鸿蒙系统安全策略比安卓更严格。5.3 抓包、启动图和几个 UI 细节抓包。查网络问题最需要的就是抓包。Charles配合鸿蒙设备抓HTTPS包的传统做法是安装Charles根证书并手动信任但HarmonyOS NEXT对用户证书的信任范围做了收紧抓包难度上升了一个量级。实际做法是先在DevEco Studio里配置一个测试专用网络配置把目标域名设为信任域再把抓包工具绑定到相应端口。这个配置只在debug包生效发布包不受影响比较安全。启动图。Flutter应用在鸿蒙上启动时会有两个阶段原生启动图和白屏之后的Flutter首帧。热搜上flutter原生启动图这个关键词点出了大家的痛点。解决方案是在鸿蒙侧配置系统启动页会在App加载早期显示同时让Flutter侧把亮屏状态关闭、直到第一帧渲染完成再隐藏。如果配置不对会出现白屏闪烁体验非常不好。TabBar点击取消动画效果。这是另一个高频率关键词。鸿蒙系统正在大力推广它自己的原子化服务和自适应动画风格Flutter默认的TabBar切换动画在鸿蒙上有时会让人觉得很跳。我们项目里做了一个小调整把TabBar的点击切换动画时长缩短到150毫秒并去掉水波纹让交互更贴近鸿蒙原生应用的感觉。代码很简单TabBar( controller: _tabController, tabAlignment: TabAlignment.fill, dividerColor: Colors.transparent, splashFactory: NoSplash.splashFactory, physics: const BouncingScrollPhysics(), )NoSplash.splashFactory去掉了点击时的大面积水波视觉上立刻干净很多。6. 性能优化、上架与进阶方向6.1 Impeller 与启动优化Flutter 3.x 系列在部分平台默认切换到了Impeller渲染引擎它解决了很多老版本Skia引擎在部分GPU驱动下的坏帧问题。但在鸿蒙上Impeller的支持是逐步推进的最初版本的Impeller在鸿蒙上对GPU驱动要求比较高个别老芯片设备上反而可能出问题。这导致优化方向有两个极端有的人关掉Impeller反而流畅有的人打开Impeller才流畅。我的经验是以真机为准不以默认配置为准。先在项目里加一套启动时根据设备型号动态选择渲染引擎的逻辑灰度验证之后再做全局开关。这个操作对代码逻辑没有侵入只需要在构建配置里控制--enable-impeller参数。启动优化方面除了换引擎还要关注Dart端的首帧逻辑。家庭账单列表页首帧会触发本地数据库查询和网络同步两个操作如果处理顺序不当白屏时间就会加长。我做的优化是先在UI层面用缓存数据渲染出基础页面再异步刷新最新数据。用户可以立即看到上一次的账本内容不感知等待过程。6.2 上架华为应用市场Flutter项目上架华为应用市场核心就是签名。DevEco Studio生成的签名证书和Flutter打包产物的签名要一一对应。操作顺序建议是在AppGallery Connect里先注册应用、申请签名证书指纹。把证书配置同步到鸿蒙壳工程和Flutter构建配置里。打Release包之前做一次完整的上架前测试尤其是检查发布模式下是否有debug印记残留在界面上。提交审核的时候如果应用有远程同步功能必须提供测试账号和说明文档不然审核横向检测专项测试很可能通不过。这里插一句华为官方会不定期推出开发者激励计划类的活动比如大家讨论比较多的应用开发者激励计划目的就是鼓励开发者把新应用或已有应用适配到鸿蒙上。如果你正好手上有Flutter项目这其实是一个很好的切入点既能把现有技术资产利用起来又能借助激励计划降低前期试错成本。具体政策细节以官网发布为准但方向肯定值得关注。6.3 还能怎么扩展第一版家庭账单共享本跑通以后我一直在想它的边界。一个是桌面端。父母可能在电脑上看账本Flutter的Windows端支持很成熟把相同代码编译一版桌面应用配合共享数据同步体验会很完整。热搜里提到的跨平台音乐管理系统、C交叉编译ffmpeg这类项目本质上也都是在不同设备之间共享同一套逻辑思路是通的。另一个是智能家居联动。鸿蒙系统的优势就在本地服务联动上未来可以让冰箱里的食材快过期这类数据和家庭账单打通自动生成采购建议并预估开支。这个方向还没动手但Flutter的社区版支持配合鸿蒙的元服务特性理论上很有想象力。还有一个是多账本切换。现在项目只支持一个家庭组如果一个人同时管自住房和出租房两套账就需要账本分册。这个改动主要在数据模型上加一层bookId其它逻辑基本可以复用。等用户量起来之后再考虑也不迟。最后分享一点个人经验。日常看Flutter相关文章很多内容都在讲怎么把页面写得华丽但真正做完这个跨平台鸿蒙项目之后你会发现页面华丽是最不值钱的部分。最有价值的反而是那些容易被忽略的系统差异网络栈的安全策略、插件的平台通道兼容性、渲染引擎的硬件适配、以及输入法在各系统上的行为差异。做家庭账单共享本的过程中我至少有一半时间不是在写业务代码而是在处理这些系统差异导致的偶发问题。如果你正准备把Flutter项目引入鸿蒙建议做好这个心理准备同时也别被吓到——这些坑虽然多但每一个都有迹可循逐个击破之后你会对整个Flutter生态的运行机制理解得比以前深刻得多。
返回列表