ARTICLE DETAIL

资讯详情

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

Flutter在OpenHarmony上打造手语学习App:个人中心全流程实战

Flutter在OpenHarmony上打造手语学习App:个人中心全流程实战 手语学习这条垂直赛道我一直觉得被低估了。听障群体、特教老师、窗口服务人员和想学第二语言的手语爱好者需求非常明确但市面上能把“学”和“练”真正串起来的App并不多。这个项目是在OpenHarmony设备上做手语学习App技术栈选了Flutter标题里的核心其实是两件事Flutter怎么在OpenHarmony上跑稳以及个人中心这个模块怎么从UI到业务彻底打通。个人中心听起来像常规页面但它要承接登录态、学习进度、收藏数据、设置项还要跟课程、练习记录这些核心业务交互做得好不好直接影响留存和使用深度。这篇文章不会写成官方文档式介绍而是我在实战中踩坑、调优、重构之后的系统总结。你会看到完整的工程搭建方式、个人中心从需求到实现的拆解思路、状态管理选型、原生能力对接细节以及那些网上搜得到但解释不透的报错处理办法。如果你正在调研Flutter适配鸿蒙的可行性或者想把手语学习这类垂直教育App的个人中心做扎实里面大部分内容可以直接抄作业。1. 项目背景与技术选型1.1 手语学习App的核心需求与用户场景开始写代码之前我先把目标用户和核心场景拉了一个表。手语学习的用户画像很分散但每个群体的学习诉求差异不小。听障人士的亲属需要快速掌握日常交流手语特教老师需要批量管理教学内容和学生进度银行、医院、政务窗口的工作人员则需要跟业务相关的专项手语。这意味着App不能只做一个简单的视频播放器必须要有“学、练、测、记”四个环节。然后是个人中心在这条链路里的角色。没有个人中心之前用户学完就关下次打开还要重新找课程学习进度散落在各页面留存很差。加了个人中心之后它要把学习时长、已学手势数、连续打卡天数、收藏列表这些数据聚合起来变成用户每天愿意打开看一眼的“学习仪表盘”。连续打卡和进度可视化对学习类产品的影响很直接用户看到自己学了13天、掌握了40个手势复访意愿会明显增加。所以个人中心不是花瓶页面它是整个业务的数据枢纽。1.2 为什么选择Flutter跨端方案项目启动时对比过RN、uni-app、Flutter和纯原生双端方案。手语学习App的特点是视频教学占比高、手势演示动画多、需要跟摄像头和传感器做实时交互也有一些数据统计图表。RN和uni-app在UI一致性上依赖各端WebView和原生组件桥接在复杂交互动画上容易露馅。Flutter走的是自绘渲染UI一致性最好Dart语言的AOT编译在性能敏感场景下也够稳。更重要的是跨端覆盖。团队规模不大没有余力同时养Android、iOS和鸿蒙三套原生团队。Flutter在OpenHarmony上已经有社区适配分支一套代码可以把Android、iOS、鸿蒙都覆盖到对中小团队来说性价比很高。代价是鸿蒙侧的Flutter引擎分支大多是社区维护升级Flutter版本时可能要等适配周期这个风险必须在排期里留buffer。另外跟RN比Flutter的插件生态更成熟虽然部分插件在鸿蒙上还得自己补通道实现但整体可控。1.3 OpenHarmony适配现状与整体架构现在Flutter跑在OpenHarmony上最常用的路线是用社区维护的flutter_flutter分支它对应的ohos分支里集成了Flutter引擎的OpenHarmony embedder层。整个架构可以分成三层最上层是Dart写的UI和业务逻辑中间是Flutter Engine负责渲染、平台通道、Isolate管理最底层是OpenHarmony的embedder它要做的事包括创建原生窗口、把触摸事件喂给Flutter、桥接生命周期。开发调试时工程结构会比普通Flutter项目多一个ohos目录这是一个完整的OpenHarmony原生工程。用DevEco Studio打开这个目录签名、权限、构建都在这里配置。安装调试用hdc命令查看日志用hdc shell hilog。这里我踩过的一个小坑是一定要把Flutter引擎版本和OpenHarmony SDK的API版本对应起来否则构建能过运行时直接black screen或者原生so加载失败。2. 工程搭建与环境配置2.1 Flutter鸿蒙开发环境准备搭建环境的第一步不是执行flutter create而是先把工具链理清楚。我用的组合是一个支持ohos的Flutter SDK分支、OpenHarmony SDK、DevEco Studio、Java环境以及hdc工具链。Flutter SDK分支获取方式不展开跟着仓库的README操作就行但拿到之后一定要跑一下flutter doctor确认环境健康再执行flutter devices看看能不能识别出OpenHarmony设备。网络环境这里必须提一句。国内拉取Flutter引擎产物和pub包经常超时我建议在环境变量里加上两个镜像地址PUB_HOSTED_URL和FLUTTER_STORAGE_BASE_URL。这一步不做后面很多依赖下载会反复失败而且报错信息不带任何提示容易让人误判是代码问题。以前我有个同事在这上面卡了半天最后发现是墙内下载flutter_build产物超时这种基础环境问题越早解决越省时间。版本匹配是个细致活。OpenHarmony SDK的API版本和Flutter引擎分支有对应关系打个比方你不可能用一张只支持Android 12的停车卡去刷Android 14的车库。我在项目里维护了一个readme文件记录每个开发机使用的SDK和分支commit方便团队新成员一键复现环境。这个习惯在跨端项目里特别重要环境不一致导致的行为差异排查起来比业务bug痛苦得多。2.2 创建Flutter工程并接入OpenHarmony环境就绪后创建工程的步骤比想象中简单命令跟普通Flutter项目一样执行flutter create --org com.example sign_language_app然后把工程根目录下生成的ohos目录用DevEco Studio打开。Android Studio用户不用担心这个过程的思路跟AS打开android目录一模一样只是编译目标变成了鸿蒙设备。打开ohos工程后需要做三件事。第一是配置签名真机调试必须有签名DevEco Studio里有一套自动签名流程跟着向导走就行。第二是声明权限手语学习App要调相机做手势识别要麦克风录音采集发音还有网络请求这三个权限在module.json5里一个都不能少漏一个到运行时才报错排查成本高。第三是设置构建参数有些版本需要显式指定flutter的root否则构建脚本找不到Flutter SDK。构建流程上我习惯在DevEco Studio里触发完整构建它会自动把Flutter产物打包进鸿蒙工程。第一次构建会比较久主要是要编so文件和资源后面会走缓存。装到设备上用的是hdc工具类似adbhdc install命令装上后通过hdc shell hilog实时看日志。这里提醒一下不要同时在DevEco和命令行里混着发hdc命令偶发设备断开问题会让人误判成代码崩溃。2.3 项目目录结构与基础封装工程搭好之后目录结构我按业务和基础能力拆成了两层。lib目录下分models、pages、widgets、services、states、utils几个子目录。pages放页面级代码widgets放可复用组件states放状态类services放接口层utils放通用工具。这个分层不复杂但对后续扩展很关键业务逻辑不会堆在widget里。基础封装这块我强烈建议一开始就把三样东西做掉BasePage、ApiClient和AppTheme。BasePage统一处理页面生命周期、埋点和通用loading/error视图后续每个页面都能继承省掉大量重复代码。ApiClient封装Dio统一baseUrl、超时、token注入和异常转换手语学习App的接口风格比较统一封装一次能管住全站网络请求。AppTheme统一字号、色板和圆角个人中心这种UI密集型页面最怕风格失控规则定好开发效率会高很多。路由层面我用的是go_router。个人中心的子页面比较多课程详情、学习计划、错题本、设置页都要能带参数跳转还得支持退出登录后清栈回登录页。go_router的stateful shell和redirect机制很适合这个场景比裸Navigator好管。3. 个人中心模块需求拆解与UI实现3.1 个人中心的信息架构设计个人中心的页面分区我按照“我是谁、我学得怎么样、我能干什么、我怎么管理账号”四段式设计。顶部是账号资料区包含头像、昵称、ID、等级积分和编辑入口。往下是学习数据区放学习时长、已学手势数、连续打卡天数、收藏数量这是整个页面的睛点用户打开App第一眼看到的就是这些数字。再往下是功能入口区学习计划、错题本、我的收藏、消息通知这些都是跟学习业务强相关的低频操作。最底部是设置区账号安全、隐私、关于版本、退出登录。这样分区的逻辑很直白首屏最有价值的位置留给身份和数据把用户最关心的“我今天学了什么、坚持了多久”放在第一屏功能入口放在中部低频设置沉底。整个页面我做的可滚动单页不套Tab避免分层过深。移动端个人中心最忌讳把一级功能塞进二级页面用户点三下还找不到常规入口就会烦躁。3.2 用户头像与资料卡片的实现资料卡片的UI实现我用的是Container加LinearGradient做一个渐变背景高度根据内容自适应然后在上面叠头像、昵称、等级和编辑按钮。头像用的是CircleAvatar加NetworkImage这里有几个实用技巧网络图要加缓存策略加载失败要有默认头像兜底避免破图影响视觉。如果头像URL可能失效我建议不要直接让Image.network裸奔而是封装一个CachedAvatar组件内部处理loading、错误和缓存。用户信息的展示布局我用Stack把编辑按钮浮在右上角下面用Row排昵称和等级徽章再用一行小字展示ID或签名。点击编辑按钮会弹出showModalBottomSheet底部弹窗里面放一个TextFormField表单校验昵称长度和签名长度保存后通过回调更新页面状态。这里有个体验细节昵称输入框默认聚焦键盘弹出时页面要上移我用MediaQuery处理了viewInsets不然键盘会挡住提交按钮。编辑保存后内存里的用户模型要立刻更新UI通过BlocBuilder自动刷新。这条链路必须走状态管理不能只改页面局部变量否则从个人中心跳去学习页再返回数据会回退。这是我第一版踩过的坑后面会细说。3.3 学习进度与数据统计卡片统计卡片的视觉样式我用的是GridView.count做2x2布局每张卡片是一个独立的渐变色块左上角放icon中间放数字下方放标题。比如学习时长卡片用蓝色渐变已学手势用绿色渐变连续打卡用橙色渐变收藏数量用紫色渐变。颜色区分能让用户扫一眼就定位到自己关心的数据比单一白卡片更直观。卡片背后的数据结构是一个StatItem模型包含icon、label、value、target四个字段。整个统计区由一个List 驱动渲染新增指标只需要加一条数据不用改widget结构。每张卡片点击后跳到对应详情页学习时长有学习日历已学手势有手势列表连续打卡有打卡记录。详情页的数据接口是独立的但首屏显示的数字要在个人中心接口里一次带回减少请求次数。这里我想提一个容易被忽略的问题统计数字的加载态和错误态必须单独设计。网络慢的时候数字不应该显示0或者空白我用的是骨架屏加状态恢复下拉刷新时可以重新拉取。另外数字更新要平滑比如学习时长从67分钟涨到72分钟直接跳变会显得假我加了一个简单渐变动画体验好很多。3.4 功能列表与设置项的实现功能列表区我用模型驱动渲染。定义MenuItemModel包含icon、title、trailing和onTap回调然后用ListView.separated统一渲染。这样写的好处是新增入口只需要改数据源不用复制粘贴一堆ListTile。功能列表的item之间用分隔线隔开间距统一8像素整体呼吸感会好一些。设置项区我做了两个分组账号安全组包含修改密码、绑定手机、登录设备管理通用组包含通知设置、关于版本和退出登录。版本号用package_info_plus动态获取不写死字符串不然每次发版都要改代码。退出登录我做了二次确认的Dialog文案明确提示退出后需要重新登录避免用户误触。退出登录的逻辑链路比较关键先清掉本地缓存的token和用户信息再通过路由跳到登录页同时把页面栈清空。这里要特别小心flutter的root navigator页面栈清理不彻底会导致登录后按返回键又回到个人中心。我在go_router里配置了redirect未登录状态下访问受保护页面会自动跳到登录页这样从根上防止权限穿透。4. 状态管理与数据打通4.1 用Cubit管理个人中心业务状态个人中心的状态管理我选了flutter_bloc里的Cubit而不是完整的Bloc。原因是这个模块的状态并不复杂无外乎loading、loaded、error三种用Bloc的Event类组织会显得杀鸡用牛刀写一堆样板代码。Cubit的方法本身可以当作事件处理函数一个方法对应一个动作代码量少、易读。状态类我用Equatable做了值比较。UserProfileState包含status、user和error三个字段copyWith方法负责生成新状态。UserCubit里面暴露loadProfile、updateProfile、logout三个方法分别emit对应状态。UI部分用BlocBuilder监听状态变化根据status切换loading视图、内容视图和错误视图。listenWhen和buildWhen要配好避免无关状态变化触发重建比如用户修改头像时status还是loaded头像字段变了才应该刷新UI。这里有一个经验Cubit里不要直接new一个状态类一定要通过state.copyWith去更新否则会丢失其他字段的值。我第一版偷懒updateProfile时直接emit了新状态结果status被冲掉页面瞬间变loading再变回来肉眼可见闪烁。4.2 异步时序与Future回调的细节Dart的异步模型有个容易踩坑的点Future.then的回调确实会进入微任务队列但它不是手动写一个await就能保证时序的。个人中心经常遇到这个场景登录页返回后个人中心需要刷新数据而数据接口和本地缓存是并行触发的。如果代码里写成了Future.wait两个请求一起等其中一个失败会导致整个回调异常。我改成分别等待各自有错误兜底用户体验更好。另一个坑指向Cubit内部异步操作。Cubit在异步方法里await之后emit状态如果这时候widget已经销毁、cubit已经closeemit会报错。flutter_bloc新版会智能丢弃无效事件但为了保险我在异步回调里加了state检查主动判断cubit是否还活着。这个坑在列表页下拉刷新时很常见页面切走了回调才回来一emit就炸。关于组件通信个人中心页和课程详情页之间需要同步学习进度我没有引入全局状态库而是定义了一个轻量EventBus。课程完成学习后post一个事件个人中心监听后刷新统计数字。EventBus的优点是简单直接缺点是事件多了之后难追踪所以我在代码里限制了事件类型枚举保证不滥用。4.3 页面状态保持与导航栈管理底部Tab切换导致个人中心状态丢失的问题是社区高频问题。起因是Tab切换默认会销毁不显示的页面重新进入时State会被重建所有数据要重新拉取滚动位置也会丢。我在项目里没有用IndexedStack因为四个Tab页面都进内存对低端设备不友好。最终方案是让个人中心State混入AutomaticKeepAliveClientMixin把wantKeepAlive设为true再配合NavigationBar的keepAlive配置这样Tab切换时页面和滚动位置都保住了。但KeepAlive也不是万能的领取了keepAlive的页面永远活着数据更新后就无法从视图层强制刷新。我在keepAlive的页面里做了一个自定义通知业务数据变化后手动触发列表更新而不是依赖生命周期重建。如果你也要用KeepAlive记住它解决的是Widget状态保存问题不是数据同步问题。Navigator压栈跳转也会导致状态丢失但不是同一个丢法。A页push到B页B返回A后A的State默认还在只是不会自动刷新。如果A页面显示的数据依赖B页面的操作结果就得在push时加一个.then回调返回后拉新数据。个人中心的学习计划页就是这个场景新增计划后回来列表必须更新我用callback而不是initState去刷新效果很稳。4.4 登录态持久化与接口拦截登录态的持久化方案我选了shared_preferences轻量且跨端一致。启动后先读取本地缓存的token和user JSON有数据就直接恢复登录态同时后台静默刷新用户信息。无数据则显示登录页。这个逻辑放在App启动的初始化流程里有一次性加载的判断避免重复初始化。接口拦截统一放在Dio的Interceptor里。请求前自动加Authorization头token不存在就阻断并跳登录。响应遇到401表示token失效先尝试用refreshToken换取新token拿不到就清空本地登录态。这套拦截逻辑是通用的但要注意判断次数防止刷新token失败时反复请求死循环。我加了简单的重试计数最多重试两次。个人中心部分接口的返回数据比较大如果每次都走全量刷新流量和渲染压力都不小。我的策略是本地缓存先垫底页面秒开后台请求完成后做字段级diff只更新变化的统计数字。这个策略对手语学习这种以数据展示为主的页面很有效用户感知到的流畅度是质的提升。5. 手语学习核心功能与原生能力对接5.1 视频教学与手势示范的实现手语学习的核心内容载体是视频所以视频播放的稳定性和交互细节比普通视频App要求更高。课程列表页展示视频封面和标题详情页加载播放器。播放器我评估过video_player和chewie的组合但手语教学的特性决定了不能直接用默认播放器手势动作需要慢放、循环回看、镜像翻转默认控制条根本不支持这些操作。所以在视频播放这块我选择嵌入一个原生播放器作为兜底方案。对OpenHarmony设备来说原生播放器对硬解支持更好耗电和发热可控。我用PlatformView把原生播放器嵌入Flutter页面自定义控制条仍然用Flutter层画点击事件通过MethodChannel转发给原生播放器。这样UI统一、底层性能有保障。5.2 EventChannel与原生相机/传感器交互手势识别交互是另一个原生能力核心。用户跟着视频学手势时App要调起相机持续识别手部动作并给出反馈。这个场景的数据流是单向高频的从原生到Flutter不适合用MethodChannel因为MethodChannel是请求-响应模式不适合持续推流。这里用的是EventChannel。Flutter侧代码大概是这样的static const EventChannel _gestureChannel EventChannel(sign_language/gesture_stream); void listenGestureResult() { _subscription _gestureChannel .receiveBroadcastStream() .listen((event) { final result event as Mapdynamic, dynamic; if (result[gesture] ! null) { // 更新手势演示UI高亮当前手型 } }); }原生侧实现StreamHandler在onListen里启动相机识别会话识别结果通过sink.add上报给Flutter。这里有两个关键点一是EventChannel的sink不能在后台线程直接add必须切到主线程否则Flutter端收不到事件二是onCancel时必须同步停止识别并释放相机否则页面销毁后相机还开着既费电又可能闪退。用EventChannel的另一个坑是生命周期。如果你在initState里订阅、dispose里不取消事件流会一直存在内存泄漏不说页面reopen时还会出现重复订阅导致同一帧数据触发多次UI更新。我的处理是在dispose里统一cancel掉subscription并且在原生侧判断没有listener时自动停掉识别服务。5.3 PlatformView嵌入原生组件与插件适配PlatformView用来嵌入原生组件这个机制在OpenHarmony上比Android上要小心。视频播放器、自定义相机预览这些场景会用到。嵌入之后要留意两个问题一是触控事件和帧同步PlatformView在混合渲染模式下容易出点击偏移二是生命周期同步原生页面在Flutter销毁时一定要同步释放资源。第三部分关于第三方插件的鸿蒙适配流程。如果项目依赖的某个插件没有ohos实现就需要按federated plugin的模式自己扩展。流程大概是先检查插件的flutter-plugins-dependencies文件看是否包含ohos支持。没有的话就在本地创建一个只包含ohos实现的包通过依赖覆盖的方式让主工程使用本地实现。具体到代码Dart层保留接口在ohos原生工程里实现MethodChannel/EventChannel的注册和处理逻辑。像Okta这类登录插件适配鸿蒙思路也是一样的先看官方是否出适配没有就自己包一层平台通道。原生页面跳转也值得一提比如账号找回、实名认证这类流程原生体系已经很成熟没必要在Flutter里重写一遍。我用MethodChannel暴露一个native_router方法Flutter侧传入目标页面名和参数原生处理页面跳转结果通过result回调返回。这种混合栈模式很实用特别是团队原生能力更强时可以让每个端做自己最擅长的事。6. 常见问题与排查技巧实录6.1 SDK版本不匹配与构建报错项目开发中遇到的第一个高频报错是The current configured Flutter SDK is not known to be fully supported. Please...。这个错误出现的原因通常是工程pubspec.lock锁定的Flutter版本和本机安装的Flutter SDK版本不一致。比如工程是3.22创建的本机是3.27Dart SDK也升级了语法可能还兼容但引擎产物和工具链的行为检测会拦截。解决方法是执行flutter downgrade到工程要求的版本或者把工程pubspec的环境约束更新到当前SDK可接受的范围。不建议通过强改环境变量绕过DevEco后续构建还会栽在版本问题上。第二个高频报错跟Android侧关系更大you are applying flutters main gradle plugin imperatively using the apply script method。这个报错是AGP插件迁移引起的原来的apply plugin方式被废弃要改成plugins DSL。注意这个报错虽然在Android构建时出现但也会影响整个工程构建流程因为Flutter工程默认会检查android目录。改法是把settings.gradle里的插件声明和build.gradle里的apply逻辑统一成plugins块。第三个构建问题只在鸿蒙侧出现Flutter的so文件和OpenHarmony SDK版本不匹配运行时直接闪退日志会提示related so file not found。这个问题的排查方向很明确先看构建时FNOHOS的架构是否包含arm64-v8a再看native库版本能否对应上设备系统。解决是统一升级Flutter分支和OpenHarmony SDK别一个高一个低。6.2 页面卡顿与渲染问题Impelter渲染引擎在OpenHarmony上的表现我刚开始是比较乐观的实际跑下来在某些模拟器和低端设备上会出现黑屏、花屏或者纹理异常。这不是我们的业务代码问题而是Impeller对GPU驱动兼容性的要求比较高。Flutter 3.7以上在部分平台默认开启Impeller如果设备渲染出问题最简单的规避是启动时加--no-enable-impeller让引擎回退到Skia渲染。UI性能会略降但换来稳定。列表卡顿的问题更多是我们自己的锅。个人中心统计卡片如果每个build都创建渐变对象每帧都在重建Paint性能会很难看。解决方法是把渐变色定义为静态常量widget尽量const用RepaintBoundary隔离不需要重绘的区域。统计数字更新时只刷新数值Text不让整个卡片重建。这套组合拳下来列表滚动能稳定60帧。还有一个不起眼但真实存在的小问题Flutter在Web/桌面端的启动速度跟移动端完全是两码事。移动端的诉求是冷启动快Web端还要考虑引擎下发的体积。这个项目里我以移动端为核心优化目标Web端和桌面端版本不追求极致启动速度而是保证功能一致。如果你做的是多端适配最好提前区分主次场景别用移动模板去套所有平台。6.3 页面状态与交互细节问题TabBar点击取消动画这个问题是项目打磨细节时遇到的。Flutter默认的TabBar在点击tab时自带一个滚动动画和InkWell高亮但产品经理希望点击就立刻切页不要那两三百毫秒的动画。我一开始试过改physics参数又试了TabController监听加animator重置都不彻底。最后方案是放弃默认TabBar自己构建一个轻量tab切换组件用GestureDetector加setState控制当前index完全绕开动画机制。如果你也要做类似定制建议评估一下是否值得替换默认组件动画不是bug强行取消有时反而会让交互反馈变生硬。返回后页面不刷新的问题前面提过这里再强化一下。个人中心列表页从详情返回后要刷新数据的话在Navigator.push调用后面接.then这是最理所当然也最可靠的方案。另一种是用路由的observer监听返回事件但observer拿不到页面上下文处理数据同步反而绕弯。我在项目里统一用push.then页面代码里标注了TODO注释后面加数据源不影响调用的地方。底部弹窗和键盘遮挡是我觉得体验影响最大的一处。编辑个人资料的底部弹窗键盘弹出时会盖住保存按钮。处理方法是弹窗内容用SingleChildScrollView包起来再根据MediaQuery.viewInsets.bottom计算当前键盘高度动态给弹窗加padding。以及TextFormField的textInputAction设为done保存可以从键盘直接提交不用用户去点按钮。6.4 一套排查思路我在这个项目里摸索出了一套自己的排查套路对Flutter加原生的混合项目特别有用。第一步永远是看日志Flutter侧用debugPrint打点日志原生侧用hilog看系统日志。日志要区分Dart层报错、线程安全问题和平台通道调用失败三种情况对应完全不同的处理路径Dart层报错看stack trace线程问题看是否在主线程回调平台通道失败看method名和参数格式。第二步是复现并缩小范围。手语识别不好用了先在原生测试页里单独测识别功能排除Flutter层因素再用EventChannel裸流测试排除UI渲染层因素最后才怀疑是Flutter和原生跨界调用的问题。这种从最底层向上排查的顺序比直接在混合环境里乱猜快得多。第三步是给所有平台通道调用包一层Result封装。MethodChannel和EventChannel的返回类型是dynamic直接把裸数据抛给上层业务出问题了连什么类型都看不出来。我封装之后每个通道的返回都规范成统一的结构错误信息带着端侧标记日志一打出来就知道是原生报错还是Dart层解析失败。项目做到最后我对Flutter适配OpenHarmony这件事的看法变得很务实。跨端技术永远不是银弹它解决的是团队投入和覆盖面的问题但底层细节永远需要有人去填坑。个人中心这种模块代码实现难度本身不大真正难的是把用户数据、学习进度、原生能力这些端到端的链路理清楚任何一个环节断了页面就只是个空壳。如果你也在做类似方向我建议把登录态和进度统计的模型设计放在第一位这两个是个人中心的灵魂。UI样式随时可以换数据模型错了一天要返工好几次。另外第三方插件在鸿蒙上缺失是常态不要等踩坑再应急最好在技术选型阶段就梳理一遍核心依赖的适配状态给每个关键插件都准备一个Plan B。手语学习这个领域还有很大的产品空间把基础架构打稳了后续加AI手势识别、加社区互动、加多端同步都容易很多。
返回列表