1. 项目概述与整体设计思路说起跨平台开发这些年我前前后后折腾过不少方案——从早期的WebView套壳到后来的React Native热更新再到编译到原生控件的各种框架。每个方案都有自己的甜区也都有自己的坑。最近大半年我一直在跟Flutter在鸿蒙系统上的适配打交道。正好做完一个小项目一个简易童谣大全应用。这玩意儿听起来不起眼却是验证Flutter跨平台能力极好的试金石。先说说这个项目为什么值得玩。童谣应用的核心需求很单纯展示大量文本内容支持搜索、收藏、播放音频。没有复杂的业务逻辑没有高并发没有实时通信。但恰恰是这种看似简单的应用最能暴露跨平台框架在系统适配上的真实水平。你得处理深色模式、中文字体渲染、音频后台播放、本地存储、列表性能优化——这些都是跨平台开发的硬骨头。用童谣应用当载体你不需要跟复杂业务缠斗可以专注在框架能力和系统适配本身上。我选择Flutter而不是其他方案主要是看重三点自绘引擎不依赖系统自带控件理论上在所有平台渲染表现一致跨平台差异大多集中在系统能力调用上Dart语言上手快对前端背景的开发者友好AOT编译后性能接近原生鸿蒙端适配已经有相对成熟的方案社区活跃度高遇到问题能搜到解决方案再说直白一点如果把跨平台开发比作盖房子Flutter给你的是统一的砖头和水泥但你要跟鸿蒙这个新地块打交道就得摸清楚它的地基和管线布局。童谣应用虽小五脏俱全能把你和鸿蒙系统之间的所有接口界面都走一遍。1.1 鸿蒙生态适配的核心逻辑很多人一听到Flutter开发鸿蒙应用就懵了搞不清楚鸿蒙到底兼容什么、Flutter能不能直接跑。我花了大半个月踩坑之后把这里的逻辑捋清楚了。鸿蒙系统底层也就是其内核部分跟经典Linux发行版有区别但同时它提供了一套兼容层来运行Android生态的应用。这就导致了一个现象纯粹为Android写的Flutter应用理论上在鸿蒙兼容环境中可以跑但体验和性能往往不理想系统能力接入更是磕磕绊绊。真正靠谱的做法是走Flutter官方的鸿蒙适配分支把Flutter引擎直接编译到鸿蒙的原生运行环境上。这样一来Flutter就在鸿蒙系统上以原生应用的身份运行而不是通过兼容层翻译。具体到开发层面你需要用适配过鸿蒙的Flutter引擎和Dart SDK配合鸿蒙的应用打包体系来发布。整个过程我自己实践下来大致分层是这样的最底层Flutter引擎负责Dart代码的执行和UI渲染在鸿蒙上以原生形态运行中间层插件体系比如音频、存储、文件访问等能力需要原生实现或社区适配最上层你的Dart业务代码这部分跨平台复用基本不用改理解了这个层次结构你就知道为什么标题里强调的是跨平台鸿蒙开发而不是鸿蒙原生开发。你要做的是把应用的大部分逻辑交给Flutter只在系统能力调用处做适配这是当前效率最高的开发路径。1.2 童谣应用的功能定位与模块拆解既然要用童谣应用当教学载体我就先把最终做出来的东西拆解开。这个应用麻雀虽小但功能模块划分得清清楚楚每一个模块都能对应到一类典型的跨平台开发问题。童谣列表页展示所有童谣卡片带分类筛选验证长列表性能童谣详情页文学内容排版、收藏按钮、字体大小调节验证复杂布局与状态管理搜索功能按标题和关键词模糊搜索验证文本匹配与即时刷新音频播放本地音频文件和在线音频切换播放验证多媒体能力收藏管理本地持久化存储验证数据读写和状态同步深色模式适配跟随系统深色设置切换主题验证主题响应机制设置页面儿童模式开关、文字大小调节等验证多页面数据共享单个看每个功能都很简单但组合在一起就构成了一个对跨平台框架全方位考验的体检项目。我在实际开发过程中就是在这些模块里逐一踩坑、逐一解决的。下面我会按照从环境准备到具体实现再到问题排查的顺序把我记录下来的完整流程和经验教训全部摆出来。2. 环境准备与工程配置要点2.1 开发环境搭建的完整流程最开始我以为适配鸿蒙要装一堆额外的SDK流程会很繁琐实际操作下来发现并没有想象中可怕但有几个关键环节确实必须注意。第一步先安装配好的Flutter SDK版本。这里要特别提醒一下不是随便下载一个稳定版Flutter就能跟鸿蒙打包工具链顺利配合的。我一开始图省事直接用了我机器上原有的某个版本的Flutter结果构建时引擎适配报错排查了好几个小时。最后还是老老实实用了鸿蒙适配推荐的分支版本才解决。第二步安装鸿蒙开发套件。包括命令行工具和图形化IDE主要用来打鸿蒙应用包和连接真机调试。注意安装路径不要带空格和特殊字符不然后续构建脚本会有一堆奇怪问题。第三步配环境变量。需要把Flutter目录和鸿蒙工具目录加到PATH里同时设置几个必要的环境变量来指定SDK路径。这一步完成后在终端输入命令能正常输出版本信息才算环境通了一半。第四步在IDE里装Flutter插件然后把Flutter SDK路径指过去。走完这一步你新建Flutter项目的时候就能看到鸿蒙设备作为可选的运行目标。我最后用了一个验证环境是否正常的笨方法拿官方模板新建一个最简Flutter应用不加任何原生代码直接跑在鸿蒙真机上。看到默认计数器的界面老老实实显示出来再开始正儿八经的童谣应用开发。2.2 工程创建与目录结构改造环境准备好之后建工程有个细节值得说道不要直接在图形界面的新建项目向导里选设备类型为手机因为那样生成的模板默认带Android/iOS目录鸿蒙的适配工程层级需要额外手动改造。我采用的是先建标准Flutter工程然后加入鸿蒙工程的适配层。具体做法是复制鸿蒙适配模板里给的那几个关键文件和目录放进你的项目根目录再改一下配置文件里的应用名称和包名。改造完的目录结构大致长这样my_rhymer_app/ ├── lib/ # Dart业务代码 │ ├── main.dart │ ├── models/ │ ├── pages/ │ ├── services/ │ └── widgets/ ├── assets/ # 资源文件 │ ├── audio/ │ └── data/ ├── android/ # Android工程保留跨平台能力 ├── ios/ # iOS工程保留后续可扩展 ├── harmony/ # 鸿蒙工程适配层新增 │ ├── entry/src/main/ │ ├── build-profile.json5 │ └── oh-package.json5 └── pubspec.yamlharmony目录是适配鸿蒙的关键它本质上是一个鸿蒙原生工程入口会加载并启动Flutter引擎。理解了这一点你就明白为什么Flutter业务代码完全不用动只需要在原生工程里做好宿主就行。在配置文件里我需要把应用包名、版本号和应用显示名称改成自己项目的。这个包名要跟后面在鸿蒙开发者后台申请签名时的包名一致否则真机调试装不上。这是新手最容易踩的坑之一。注意修改包名时要同步检查build-profile.json5里的signingConfigs调试模式下记得勾选自动生成签名。否则就算代码全对也会因为签名问题无法安装到真机。2.3 依赖管理与原生能力打通童谣应用需要的依赖不算多但每选一个包我都很谨慎。这里给出一份实际的依赖清单并说明为什么做这个选择dependencies: flutter: sdk: flutter provider: ^6.1.1 shared_preferences: ^2.2.2 audioplayers: ^5.2.1 just_audio: ^0.9.36provider用来做状态管理。童谣应用的全局状态很轻量一个播放器的播放状态、一个收藏集合用provider轻量工具就够了不需要引入重型框架增加心智负担shared_preferences做本地存储。收藏列表和设置项都是简单的键值对不需要上数据库audioplayers和just_audio这两个看起来功能重叠了实际上我在开发时发现不同版本的鸿蒙适配插件对格式支持有差异有些音频格式在旧版播放器上放不出来。所以我留了双保险在代码里做了兼容判断后续会细讲。依赖安装完成后真正要动点脑筋的是插件的鸿蒙端实现。社区适配做得好的音频插件你只需要在工程配置里显式声明开启了鸿蒙平台支持原生实现会自动带进来——如果插件本身没有提供鸿蒙实现你得另找替代品或者自己写插件通道。我遇到一个印象很深的坑某个版本的音频插件在Android上一切正常到鸿蒙上编译时提示缺少符号。原因就是该插件还没有适配鸿蒙。这不是代码写得有问题而是插件生态覆盖范围的问题。所以我在做这个项目时养成了一个习惯——选插件之前先去查它的平台支持列表优先选明确标注支持鸿蒙的版本。3. 童谣数据处理与纯净架构设计3.1 纯Dart实现的数据层童谣应用的数据很有特点内容固定、体量大、分类明确。我选择了把所有童谣数据以JSON形式内置到应用里而不是通过网络请求获取。理由很直接——童谣内容是静态的更新频率极低做成内置资源可以保证打开应用立即就能用不依赖网络特别适合给低龄儿童使用的场景。数据文件放在assets/data目录下格式长这个样子{ version: 1.0.0, categories: [摇篮曲, 自然, 动物, 节日, 游戏], songs: [ { id: c001, title: 小星星, category: 摇篮曲, keywords: [星星, 闪亮, 天空], content: 一闪一闪亮晶晶满天都是小星星。挂在天上放光明好像许多小眼睛。, audio: assets/audio/xiaoxingxing.mp3, favorite: false } ] }用纯Dart写数据层的核心是把这个JSON加载和解析封装好。我在models包下定义了一个童谣数据类把上面的字段映射成Dart对象另外定义了一个数据仓库类负责从assets里读取JSON并解析成对象列表。这里有一个容易忽视的性能问题内置JSON的加载是同步进行的如果把一个几兆的JSON放在主Isolate里解析启动时会出现明显的卡顿。我的做法是加一个异步加载流程应用先渲染一个简单的加载页后台把JSON解析完再进入主界面。用到的核心代码非常简单FutureListRhyme loadRhymes() async { final rawData await rootBundle.loadString(assets/data/rhymes.json); final decoded jsonDecode(rawData) as MapString, dynamic; final list decoded[songs] as Listdynamic; return list.map((e) Rhyme.fromJson(e as MapString, dynamic)).toList(); }这段代码看起来平平无奇但在我实际调优的过程里它经历了两次比较大的改动——第一次是加异步第二次是加缓存。因为童谣列表页会被反复打开每次打开都重新解析一遍JSON其实是多余的。我加了一个简单的单例缓存第一次加载完存进内存后续直接取内存数据体验立刻提升了一个档次。3.2 童谣列表页的状态与渲染分离童谣列表页是这个应用的门面用户打开应用第一眼看到的就是它。我采用了一个经典的设计模式把数据状态和UI展示分离。状态部分由一个列表页控制器对象负责。它对外暴露三个东西当前分类的童谣列表、当前的分类选中状态、切换分类的方法。UI部分只负责订阅这个控制器拿到数据后渲染成卡片列表。用provider的思路来写的话伪代码是这样class RhymeListController extends ChangeNotifier { ListRhyme _currentList; String _selectedCategory; void selectCategory(String category) { _selectedCategory category; _currentList allRhymes.where((r) r.category category).toList(); notifyListeners(); } }列表页面的UI则是一个简单的ListView.builder。在build方法里只做一件事根据控制器的状态构建出对应数量的童谣卡片。这样做的最大好处是后续加搜索、加收藏状态变化都不需要改动UI层结构只需要在控制器里多写逻辑。3.3 列表项组件与复用优化童谣列表项本身也有很多文章可做。每一个卡片我设计成三块左侧是一个圆形的分类图标背景中间是童谣标题和第一句内容预览右侧是收藏状态的爱心图标。列表项的关键优化在于复用。Flutter的ListView.builder本来就会复用离屏的item但如果你的列表项组件里做了重活——比如加载本地图片、初始化音频播放器、创建不必要的Stream——复用时就会出各种问题。我的经验是列表项必须保持纯净。具体操作有三条不直接状态管理所有数据通过构造函数传入音频播放器放在页面级不放在列表项级图标和装饰都用Flutter内置组件绘制不用图片资源做到这三条列表在真机上滑动就很流畅。我也试过在列表项里放一个带阴影效果的Container在低端鸿蒙设备上滑动时会有肉眼可见的掉帧后来改成纯色背景加细边框渲染压力小了很多。4. 核心功能实现与路由设计4.1 搜索功能无阻塞输入与刷新搜索功能单独拎出来讲是因为它踩到的一个典型性能坑。最开始我直接给TextField的onChanged绑定了数据过滤逻辑用户每敲一个字就整个列表重新过滤一遍。直觉上没什么大不了但当童谣库扩充到几百条、每条内容几百字而且用户快速连续输入时会有可感知的卡顿。问题的根源是过滤逻辑在主Isolate里同步执行UI线程被长任务阻塞了。优化方案是加一个防抖器——用户停止输入300毫秒之后才真正执行搜索。这样既不丢失实时搜索体验又大幅减少无效计算。实际改动很小但对于体验的提升是非常明显的Timer? _debounce; void onSearchTextChanged(String text) { _debounce?.cancel(); _debounce Timer(const Duration(milliseconds: 300), () { controller.performSearch(text); }); }对搜索匹配的逻辑我也做了一个细节设计把搜索范围从标题扩展到关键词字段。很多童谣标题里根本没有星星两个字但内容里确实提到了。如果你只按标题搜用户搜星星就会漏掉一大片相关童谣。我让搜索先匹配标题再匹配关键词列表最后匹配正文前50个字匹配优先级依次降低。这样搜索结果的相关性排序会自然合理一些。4.2 详情页的多状态协调从列表页点击任意一首童谣就进入详情页。详情页要同时处理三种状态童谣内容展示、播放器状态、收藏状态。三种状态之间还有联动——播放时页面要显示当前播放时间和进度条收藏状态变化时列表页的爱心图标也要同步更新。这里面最简单也最容易搞砸的方案是把三种状态全部维护在详情页组件里用setState硬顶。当用户从详情页返回列表页时收藏状态就得靠页面间传值来同步。刚开始我的确这么写过很快发现不对劲——页面被系统回收后再恢复状态就丢了列表页返回时偶尔也不刷新。后来我改成了全局状态方案在provider里注册两个对象一个管收藏状态一个管播放器状态。详情页只是这两个全局状态的使用者列表页也是。收藏变化后全局状态通知所有监听者刷新页面间同步问题自然消失。4.3 路由参数传递与页面生命周期Flutter的页面跳转有两种主流方式用Navigator直接push一个路由或者用命名路由管理。童谣应用页面层级不深我用了前者——直接在列表页代码里push详情页同时把当前童谣的id作为参数传过去。代码大概长这样Navigator.of(context).push( MaterialPageRoute( builder: (ctx) RhymeDetailPage(rhymeId: rhyme.id), ), );用id而不是整个对象传参是我刻意做的设计。如果直接把整个Rhyme对象传过去当列表页刷新、童谣数据发生变更时详情页拿到的还是旧对象状态不同步。只传id详情页自己根据id从数据仓库取最新数据这个坑就能完全避开。关于页面生命周期我踩过一个比较隐蔽的坑。童谣详情页在播放音频时如果用户切到后台再切回来Flutter框架的WidgetsBindingObserver会收到生命周期变化事件。如果你不监听这个事件音频可能在前台恢复后继续乱播或者播放器状态跟UI显示不一致。我专门给详情页加了一个周期监听在应用切后台时暂停播放回到前台再恢复同时在UI上显示暂停状态。4.4 收藏功能的设计实现收藏功能涉及数据写盘、状态同步、列表联动看起来小做起来涉及面很广。收藏数据要持久化。童谣应用本身是给小朋友用的家长可能今天在这个设备上收藏了几首明天换一个平板又想接着用。考虑到这个场景还不够重要到需要上账号体系我选择了最简单本地的持久化方案用shared_preferences把收藏的童谣id列表存成JSON数组。读写的核心逻辑就是两个函数Futurevoid saveFavorites(ListString ids) async { final prefs await SharedPreferences.getInstance(); await prefs.setString(favorite_ids, jsonEncode(ids)); } FutureListString loadFavorites() async { final prefs await SharedPreferences.getInstance(); final raw prefs.getString(favorite_ids); if (raw null) return []; return (jsonDecode(raw) as List).castString(); }这样存储的优点是简单可靠缺点是每次收藏变化需要重写整个列表。童谣数量不大这个代价完全可接受。收藏状态在UI上的呈现我选择了爱心图标切换。点击前是灰色空心点击后变成红色实心同时有一个轻微的缩放动画反馈。这个动画用Flutter的隐式动画组件就能实现不需要手动控制AnimationController。小技巧列表页的收藏图标变化应该是即时的不要等返回时才刷新。用全局状态管理的话这个需求是天然满足的。如果你用的是页面间传值方案就得额外写通知逻辑非常麻烦。5. 鸿蒙适配的专项处理与指南5.1 音频播放能力的鸿蒙适配音频播放是整个童谣应用在鸿蒙上最折腾的部分。原因不复杂音频能力涉及原生系统服务不同系统的实现差异很大而音频插件在鸿蒙上的适配还在持续完善中。我在鸿蒙上实测了两种音频路径各有各的问题播放本地打包进assets的短音频文件用系统音频管理服务播放基本没问题。但部分低端设备上音质压缩明显高频部分有轻微的沙沙声播放网络音频流对网络状态切换和缓冲处理的适配不理想会出现断流后恢复慢的问题针对前一个问题我最终的解法是本地童谣音频统一转成较低采样率的M4A格式。童谣应用播放的是人声对音质要求没那么苛刻低采样率换来的体积缩减和兼容性提升是值得的。对后一个问题我在播放器外层封装了一层网络状态监听——检测到网络断开时自动暂停播放并提示网络恢复后可以手动点击继续。播放器代码里那个最关键的接口很简单但对接鸿蒙的细节都在配置里AudioPlayer player AudioPlayer(); await player.setSource(AssetSource(audio/xiaoxingxing.mp3)); await player.resume();看起来跟Android上没什么区别对吧但前提是你要保证audioplayers这个库的版本带了鸿蒙适配的实现并且在工程的配置文件里声明了鸿蒙平台的依赖。如果版本不对这段代码在鸿蒙上可能就是静默失败——不报错、也不出声卡你半天找不出原因。5.2 目录与签名鸿蒙打包的两个硬门槛打包鸿蒙应用跟打包Android完全两个路数。两个地方对新手来说最容易卡住。第一个是目录权限。鸿蒙的沙箱机制比Android更严格应用只能读写自己的私有目录访问外部存储需要特殊声明。不过童谣应用用到的静态资源都在应用内部没有跨目录需求这块没给我造成麻烦。唯一要做的是在配置里把应用所需的资源目录标识清楚否则构建时会报资源缺失错误。第二个是签名。鸿蒙应用安装到真机必须经过签名校验。调试开发时可以开启自动签名模式让构建工具帮你生成调试证书。但要注意这是跟当前开发者账号绑定的换一台电脑或者换一个账号得重新生成。签名错误的表现很有迷惑性有时候不是明确提示签名不匹配而是设备端弹出应用安装失败代码-8之类的模糊消息。我第一次遇到时愣了半天最后才想到是签名问题。排查方法很简单——去设备日志里搜签名相关的关键字有明确的报错记录。5.3 UI适配与字体渲染差异Flutter的自绘渲染带来一个好处布局是跨平台一致的你不用管鸿蒙上某个控件的默认边距跟Android差多少。但有两个跟渲染底层直接相关的点是你绕不开的。中文字体渲染。Flutter默认的字体回退机制在鸿蒙上的表现不如Android完善某些生僻字会出现显示为豆腐块的情况。我的处理是在应用入口处显式设置全局字体回退列表把鸿蒙系统自带的几个中文字体加进去。这样命中了生僻字的童谣也能正常显示。屏幕适配。鸿蒙平板和手机的分辨率差异非常大童谣应用没有做自适应布局的话在平板上会显得内容巨大或布局稀疏。我采用的做法是以逻辑分辨率750为基准在页面根部用MediaQuery获取宽高比做一个等比的字体和间距缩放系数。这样同一套代码在手机和平板上都能保持协调的视觉效果。5.4 性能监控与真机调试Flutter在鸿蒙上跑出来的性能到底如何我实测了一个指标童谣列表页在麒麟芯片的中端真机上加载并滚动200条数据帧率稳定在55FPS以上。这说明Flutter的自绘渲染在鸿蒙上表现是靠谱的。但性能好不代表可以忽视监控。我在这套应用里做了一个简单的性能监测工具利用Flutter的PerformanceOverlay开关在开发模式下显示实时FPS和渲染耗时。用它排查滚动卡顿问题时一眼就能看出是布局阶段耗时高还是光栅化阶段耗时高从而定位是组件树太复杂还是阴影效果过多。有一个性能坑必须提防童谣详情页里如果用了大段富文本并且频繁动态改变样式在鸿蒙低端设备上会出现比较明显的重排卡顿。优化方案就是把富文本切成多个静态Text组件只在真正变化时重建必要的部分别让整个页面跟着重排。6. 常见问题与排查技巧实录6.1 真机安装失败的几种情况这里集中记录我实际遇到过的安装失败问题和排查路径。真机调试阶段绝大多数装不上的问题都可以从我列的这个表里找到对照现象原因解决方案安装提示代码错误信息签名不匹配或证书过期重新生成调试证书确认包名一致安装后打开闪退Flutter引擎与鸿蒙基础库版本不匹配检查基础库版本与SDK版本对应关系安装失败提示空间不足缓存目录被占满清理设备缓存或卸载无用应用一直卡在正在安装USB调试通道不稳定换成无线调试通道或者重启开发者模式这里面最坑的是第一种。你以为是代码问题反复编译打包浪费一整天结果只是证书过期。我现在每次打包之前都会习惯性地先看一眼证书有效期30天内到期就先重新生成避免临时出问题打断开发节奏。6.2 音频播放无声的排查路径音频无声这个问题我在鸿蒙上踩了整整一个下午最后定位到的原因简直让人哭笑不得。全过程拆解出来供参考检查音频文件本身在电脑上播放正常检查代码调用顺序确认setSource和resume都执行了检查播放器状态输出的状态日志显示已经播放到尾部最后用设备端的日志抓了一下底层播放队列发现原因是音频文件用了旧版编码格式系统底层的解码器不认新格式。解决方式也不复杂用音频工具把音频文件全部转成标准M4A编码问题彻底消失。这件事给我留下一个深刻教训不要默认系统解码器万能的、跨设备能力是一致的。哪怕是一个看似标准的MP3文件在不同系统上的解码支持范围也可能不同。6.3 深色模式下的配色翻车童谣应用里我一开始的配色方案是在亮色背景下反复调过的深浅灰搭配起来很舒服。结果一开深色模式就翻车了——文字跟背景糊在一起对比度严重不足。问题出在我在很多地方写死了浅色调色板深色模式下照搬同一套值。要彻底解决得在应用里设计一套完整的主题系统根据系统亮度模式切换两组颜色值。我使用的方案是Flutter内置的ThemeData扩展。在MaterialApp外面包一层builder动态读取当前系统主题模式然后返回对应的亮色或暗色ThemeData。这样所有用官方主题组件的地方都会自动适配不需要每个页面单独写判断。注意动态主题切换以后要配合全局状态管理的通知机制。系统在深色和亮色模式之间切换时如果你不做额外处理已创建的页面不会自动重建。我用一个跟系统模式绑定的ValueListenable解决了这个问题主题变化时强制重建整个组件树。6.4 状态不同步的通用排查法童谣应用的收藏状态、播放状态、搜索状态分布在不同页面开发者最怕的就是状态不同步。我总结了一套通用的排查思路第一确认状态是谁持有。出现不同步十有八九是状态被复制到了两个地方。检查代码里是否存在同一份数据既存了全局变量又存在页面局部变量如果是请把它统一收敛到一个持有者。第二检查数据流向。全局状态变了谁负责通知UI刷新在provider方案里这个链路是清晰的只要持有者调用了notifyListeners监听者就得刷新。如果你发现UI没刷新第一反应应该是监听有没有建立而不是去看UI的build方法。第三看状态的初始化时机。启动时就要用全局状态的地方比如收藏列表必须保证初始化完成后再渲染页面否则会先渲染一版空数据再均值刷新造成视觉闪烁。这三步走下来绝大多数状态不同步的问题都能定位。剩下那种查不出来的大概率是毫无头绪地往组件里塞了太多共享可变对象——那就是当时没忍住偷懒挖下的坑只能重新梳理设计了。7. 项目交付心得与可扩展方向这个童谣应用从立项到跑通全部核心功能前后花了我大概两个星期的工作日。对熟手来说最核心的路线已经跑通了真正系统级适配的坑也就那么几个。快速复盘一下真正有价值的内容集中在三点纯Dart层的代码基本没有为鸿蒙多写一行跨平台的价值得到验证真正要花心思的是插件选择、签名打包和原生能力接入性能优化与UI细节的坑跟它在什么系统上跑关系不大泛用性强。做完这个项目之后我还留了一个扩展的思路。童谣内容的获取将来可以改成远端动态更新在应用内置一个内容目录启动时检查更新。收藏功能下一步可以扩展成跨越设备的同步那就要引入云服务了。但说实话这个功能要是真做了应用的架构就要上一个台阶不是现在这个简单demo该背的担子。最后再分享一个小技巧做跨平台应用时不管目标是哪个系统我都建议先用一组固定测试用例——列表滚动性能、图片加载速度、音频播放稳定性、深色模式效果——在所有目标平台上统一跑一遍。很多时候你觉得代码没问题但换一个平台就是会有千奇百怪的表现。提前把基线摸清楚后面适配起来心里才有底。
阅读完成 · 觉得有帮助?