
1. 需求拆解与技术选型这个APP到底做什么1.1 演讲稿生成器的核心功能画像事情得从我们项目组接到的一个需求说起。当时几个部门都在准备季度汇报领导层希望我们做一个内部工具让员工输入演讲主题、目标时长、场合类型就能自动生成一份结构完整、口吻合适的演讲稿。说白了就是一个“演讲稿生成器”。早期我们直接把模型接口封装成一个网页但很快发现几个问题网页版在手机上的体验一般离线用不了而且大家写稿子经常是在通勤路上、会议室开场前这种碎片时间需要打开即用、写完即走。于是“做一个能用手机直接打开的轻量工具”就成了刚需。再看这个工具的核心功能其实比想象中清晰输入主题词、演讲时长、演讲场合比如“工作汇报”“新人自我介绍”“产品发布会”系统自动生成演讲稿初稿提供结构化编辑能力用户可以在生成的稿子上改词、换段落、调整口吻而不是只给一坨不可改的纯文本稿子生成后能按字数估算演讲用时方便控制时长离线或弱网场景下历史稿子能正常查看和编辑最好能有语音朗读辅助排练功能毕竟演讲稿不是用来读的是用来“讲”的。把这些需求列完团队里很自然就出现了一个分歧是三个端各写一套原生还是统一用跨平台方案。1.2 为什么选Flutter做鸿蒙端当时我们手里的目标平台是三个iOS、Android还有刚被划入交付范围的鸿蒙设备。如果走原生路线意味着同一套功能逻辑至少维护三份代码再加上后续迭代时的联调成本小团队根本吃不消。所以跨平台方案几乎是唯一合理的选择。跨平台方案里我们对比过Web容器套壳、React Native、Flutter三路。Web套壳开发快但性能和原生交互差距大特别是语音朗读、文本选中这类高频操作体验会明显打折。React Native在鸿蒙生态上的适配成熟度当时还参差不齐遇到问题容易卡住。而Flutter因为自身渲染引擎不依赖系统控件理论上只要能跑起来UI在各端的一致性是最高的。用一句大白话解释Flutter把“界面怎么画”这件事完全握在自己手里视图不是调用系统原生的控件去拼而是自己用渲染引擎把每一个像素画出来。所以同一套Flutter界面在iOS、Android、鸿蒙上呈现出的视觉效果几乎一模一样的。对我们这种文本编辑、阅读、预览为主的工具型APP来说这种一致性太重要了。鸿蒙端的适配现在已经有较完整的社区方案。基于这套方案我们最终锁定了技术路线UI层统一用Flutter编写覆盖iOS、Android、鸿蒙三个平台鸿蒙端的系统能力比如系统分享、文件存储通过专门的能力桥接层调用演讲稿生成、文本处理、字数统计等核心逻辑全部放在Dart层不碰任何平台私有API。选型做完后我心里其实有底这个项目最难的从来不是“怎么写演讲稿”而是“怎么让一套代码在多端都跑得顺、用得好”。Flutter在这里的价值往大了说是降低了多端交付的复杂度往小了说就是让我们几个人在有限时间内真的能把东西做出来。2. 鸿蒙适配环境搭建与工程改造2.1 开发环境准备鸿蒙开发第一关不是写代码而是把环境配到“能跑真机”的状态。我们踩了一轮坑之后把标准流程固定成了下面这几步照着走基本不会卡太久。第一步安装DevEco Studio这是鸿蒙应用开发的官方IDE支持创建工程、管理签名、连接真机调试。注意安装时把SDK组件选全尤其是HarmonyOS SDK和Toolchain相关的部分后面编译才不会被缺失组件卡住。第二步准备Flutter SDK和Dart SDK。这里建议直接用稳定版渠道别追新因为新版本和鸿蒙适配层的兼容性需要额外验证没必要在开发期给自己添乱。第三步在终端执行环境检查命令确认Flutter能识别到鸿蒙设备flutter doctor如果输出里没有报错说明基础环境没问题。接着把手机开启开发者模式、打开USB调试用数据线连上电脑执行flutter devices能看到设备列表里出现鸿蒙设备名称就说明连接成功了。第四步配置适配层依赖。在Flutter工程里需要引入社区提供的鸿蒙适配依赖然后同步一下工程依赖让构建系统自动拉取相关的适配代码。这个过程需要耐心首次同步可能要下载不少依赖包网络不好时容易卡住建议提前配好镜像源。上面这套流程跑完之后你其实已经有一个能跑的Flutter鸿蒙壳子了。很多新手在这一步会反复失败原因多半出在SDK版本不匹配或依赖没同步干净修复思路很直接先执行flutter clean再把pubspec.lock删掉重新拉依赖能解决大部分玄学问题。2.2 工程结构适配要点环境跑通只是开始真正头疼的是把工程结构改成“三端友好”。我们一开始的工程是纯Flutter模板后来为了接入鸿蒙适配层做了这么几件事。第一件事确认目录里是否包含鸿蒙宿主工程目录。Flutter标准的工程结构里原本只有android、ios、web这些宿主目录接入鸿蒙后需要多一个鸿蒙宿主目录。创建好之后打开其中的配置文件把应用名称、包名、版本号等信息对齐到和其他平台一致。第二件事配置权限声明。演讲稿生成器涉及存储、网络、语音合成这几类能力在鸿蒙端的权限配置文件里需要声明对应的权限项。这里有个容易踩的坑权限声明只能说明“你需要这个能力”真正弹窗询问用户是运行时系统自动处理的但如果你连声明都没写那运行到相关功能时会直接静默失败而且log里往往只给一个很模糊的错误排查起来相当费时。所以我们当时的做法是先把需要的能力全部梳理成清单集中核对一遍权限声明再开始写业务代码。第三件事签名配置。鸿蒙真机安装APP必须有签名文件调试阶段可以用自动生成的自签名证书但要注意包名必须和证书里的信息一致否则安装时直接报错。我们团队第一次遇到这个问题时检查了半天代码最后发现只是包名大小写写错了这个细节建议提前留意。第四件事检查构建产物路径是否正常。鸿蒙适配层和Flutter标准的Android构建走的是两条打包链路所以脚本里的输出目录、JAR包和SO文件的引用方式都要做对应调整。我们在这一步卡过两次后来总结出一个稳的办法构建完成后去产物目录里检查一下是否生成了对应的Flutter资源文件和SO库文件只要这两样在安装到手机上基本就能正常跑起来。等到这些基础工作做完工程才算真正具备“跨平台”的雏形。后面写业务代码时你就不会再想“我是在给鸿蒙写APP”还是“我在给安卓写APP”而是老老实实回到Flutter的世界里一门心思敲Dart就行。3. 演讲稿生成器核心模块怎么落地3.1 结构化生成引擎的设计思路演讲稿生成器最核心的部分不是UI做得多炫而是“生成”这一下到底能不能用。我们用了一个很朴素的方案不依赖单一的大模型接口去生成长文而是先用模板引擎搭好骨架再逐段调用生成能力来填充内容。为什么这么做因为纯靠模型直接给全文容易出现两个毛病一是结构飘忽有时写得像散文、有时像议论文二是字数不好控制让写800字结果给了1800字讲都讲不完。我们想要的是一个结构稳定的稿子开头、三个分论点、两个案例、一个收尾说好五个段落就是五个段落用户拿到手上能直接改而不是从头开始返工。具体做法是预置一套“演讲稿结构模板”里面定义了段落类型、顺序、每段的功能说明。生成的时候先把用户输入的主题和场合类型映射到模板上确定每一段该写什么角度例如第一段开场白点题说明今天讲这个话题的原因第二段分论点一结合用户所属领域的痛点展开第三段分论点二给出解决思路或案例第四段分论点三做观点升华或行动号召第五段收尾总结全文致谢。骨架确定后系统再为每一段生成具体内容。这样做的好处是即使某一段生成质量稍有波动整体文章仍然从头到尾都“像一篇演讲稿”用户可以针对某一段单独重新生成而不是点一次按钮就推翻全稿。模板的核心逻辑用Dart写出来其实不长核心就是定义一个段落结构列表然后逐一生成填充分数到对应的内容槽里class SpeechSection { final String title; final String purpose; final String? content; SpeechSection({required this.title, required this.purpose, this.content}); } SpeechDraft buildOutline(String topic, SpeechType type) { return SpeechDraft(sections: [ SpeechSection(title: 开场白, purpose: 点题并建立联系), SpeechSection(title: 分论点一, purpose: 提出核心问题), SpeechSection(title: 分论点二, purpose: 给出解决思路), SpeechSection(title: 分论点三, purpose: 案例佐证), SpeechSection(title: 结尾, purpose: 总结激励), ]); }生成每一段时我们会把当前段的“目的”、前后段的摘要、用户提供的主题词一起作为输入条件这样段与段之间是有上下文关联的不会出现上一段还在聊成本、下一段突然跳到员工福利这种割裂感。3.2 字数控制与时长的换算逻辑演讲稿生成器还有个容易被忽视的点字数管理。很多人写稿时根本不知道“讲10分钟需要多少字”如果工具生成的稿子不讲时长那这个工具就废了一半。计算逻辑并不复杂正常中文演讲语速大约每分钟220到260字比较从容的讲解语速约每分钟180到200字。为了避免用户念得太赶我们按每分钟200字作为基准值来估算目标字数int estimateTargetWordCount(int minutes) minutes * 200;假设用户想讲10分钟目标字数就是2000字左右。我们会把这个目标字数按权重分配到各个段落上开场白占15%分论点一占25%分论点二占25%分论点三占20%结尾占15%。这样每段都有独立的字数上限生成时模型就知道“这段别写太满”字数控制准确率会高很多。当然真实世界里用户的语速差异很大有人讲话天生快有人喜欢停顿。所以APP里专门做了语速调节用户可以选“慢速”“标准”“快速”三档系统会根据选项调整每分钟基准字数重新估算目标篇幅。实测下来这个设计非常实用至少帮我们砍掉了一半“稿子太长讲不完”的抱怨。3.3 页面流转与多端自适应演讲稿生成器的界面布局不算复杂核心就三个页面生成参数页放标题输入框、场合选择器、时长滑动条、语速选择器底部一个大的“生成稿件”按钮稿件预览页生成的稿子分段落展示每段旁边有“重新生成”“编辑”“朗读”三个操作入口稿件编辑页点击任意段落进入可以自由修改文字改完自动保存回预览页。三个页面放在手机上正好一屏一页不需要复杂导航。但在平板上一页一屏就显得太浪费空间了我们用了响应式布局屏幕宽度超过一定阈值时左边是段落列表右边是详细编辑区一次可以看稿、选段、改字效率比手机端高不少。字体大小和行距也没少花功夫。演讲稿和普通阅读文本不同用户是要“看着念”的所以每段的行高必须宽松段间距要大关键句要能加粗或标色。我们在预览页做了“提词模式”开启后每行只显示几个词根据设定的语速自动滚动像提词器一样。这个功能一开始觉得是锦上添花结果内部试用时好几个同事点名夸成了亮点功能。3.4 本地存稿与离线可用演讲稿生成器这种工具最怕的就是“网络一断稿子全没了”。我们做了两级存储正在编辑的当前稿子实时写入本地数据库历史稿子也全部存本地只把“生成”这一动作放到云端处理。本地存储用的是一种轻量级的键值数据库方案读写速度快适合存文本数据。每次用户停止输入后做一次防抖保存把当前稿子的标题、内容、生成参数、修改时间存下来。历史列表按修改时间倒序排列支持搜索标题支持删除删除前弹确认框以免误操作。离线状态下用户打开APP能看到所有历史稿子能正常编辑、复制、朗读。唯一不能做的是“生成新稿”。我们在生成按钮上做了状态判断如果检测到网络不可用直接提示“暂不支持离线生成请联网后再试”而不是让用户点了一下没反应干着急。这个“先保证已有内容可靠再追求在线能力”的思路后来被证明特别正确。因为我们有不少用户是在飞机上、地库里用APP网络条件很差最终还是顺利完成了排练准备。4. 语音朗读与导出分享怎么做好用4.1 朗读功能的异步处理演讲稿生成器如果只有“生成文字”其实和记事本没什么区别。真正让它像个演讲稿工具的是把“写”和“讲”串起来。语音朗读在这个链条上就是桥梁。朗读功能实现上并不复杂核心是调用系统的语音合成能力把当前段落按字符串传给底层即可。但有几个细节会影响体验值得单独说。第一异步合成。长文本一次性丢给合成接口经常会出现内存飙升甚至卡死的情况尤其是演讲稿动辄一两千字。我们的做法是分段合成把当前段落按句号、逗号拆成句子列表一句一句地合成音频合成完一份就播放一份播放完再合成下一份。这样内存占用始终在一个很小范围连续朗读十分钟也不会卡顿。第二朗读中的高亮跟随。当正在读某一句时页面里对应文字会加底色方便用户跟着看。实现方式也不难维护一个“当前朗读的句子序号”句子切换时更新状态UI层用AnimatedContainer做背景色过渡。这个功能对排练特别有用眼睛可以一直盯着屏幕找节奏。第三语速和音色切换。由于演讲场景经常是“正式”“轻松”“激情”三种语气来回切我们在设置里放了音色选项用户可以给不同段落的朗读选择不同音色这在排练时能明显帮助找到自己该用的语调。4.2 导出文本与提词卡片稿子写好了总要带出去。导出功能我们做了三条路径覆盖最常见的实用场景。第一条路径是复制全文。点按钮后整篇稿子以纯文本格式进剪贴板用户可以粘贴到任意聊天工具、笔记软件里最原始也最通用。第二条路径是导出提词卡片。这是我最推荐的一个功能做法是把每段文字按“句子”或“短句”重新排版成一张窄长的卡片字体大、行距宽适合放在手机屏幕上近距离看。说实话正式演讲的时候大多数人根本不会照着全稿念而是扫一眼关键词就能想起来接下来讲什么。提词卡片恰好就是干这个用的卡片上每个短句对应一个要点提示性极强。第三条路径是保存为本地文件。生成文本后以指定格式保存到手机存储目录这样用户可以把稿子拷到电脑上继续排版或者发给别人协作修改。导出时要注意给文件起一个有辨识度的名字我们用的是“演讲稿_主题_日期”这个命名格式否则用户导出几篇稿子后文件列表会乱成一锅粥。4.3 分享能力与系统集成分享这块在鸿蒙端走的是系统分享能力通过桥接层调用系统分享面板把文本或文件内容交给用户选中的目标APP。这里有个值得说的桥接设计Flutter侧只定义一个通用的“分享”方法传入文本内容和文件路径具体的分享面板展示、回调处理全部由鸿蒙原生侧负责。这样Dart代码完全不用关心系统差异只要约定好接口就行。调试分享时我们踩过一个典型的坑分享面板能弹出但选完微信或备忘录后发现分享出去的文本是空的。排查了很久最后发现问题出在“异步结果返回的顺序”上——Flutter侧在原生面板还没处理完时就读取了返回值读到了一个空内容。解决办法是在原生侧把分享结果通过回调通道异步通知Flutter侧Flutter拿到回调后再去更新UI而不是同步等待结果。这个坑很隐蔽但也很有代表性。跨平台开发里凡是涉及原生和Dart层频繁往返的场景都要下意识养成分步回调的习惯不要假设“我调你、你马上给我结果”这种同步关系成立。异步处理虽然要多写几行代码但稳定性和可维护性都会好很多。5. 常见问题与排错实录5.1 构建期问题跨平台开发的经典难题构建失败能占掉开发期三分之一的时间。我们遇到过的问题主要集中在下面几种首次拉依赖超时。解决办法是指定国内镜像源并把超时时间调长。如果还不行删除pubspec.lock再重新拉往往能解决。内存不足导致构建崩溃。鸿蒙和Android的构建工具都很吃内存构建时把其他大型软件关掉或者在编译配置里把并行任务数调低能显著减少崩溃概率。构建产物缺失Flutter资源文件。原因一般是宿主工程的资源配置不完整检查一下是否把Flutter的编译产物路径正确挂载进了宿主工程必要时先执行一次flutter clean全量重编。这里还有一个容易踩的坑如果你既开过Android模拟器又连着鸿蒙真机构建时可能出现“平台冲突”——构建脚本分不清到底该把产物打包给哪个平台。我们在开发中规整出一套习惯改需求时就只开真机调试做UI验证时才开模拟器避免误触发渠道混淆。5.2 运行期问题与权限相关坑构建通过只是第一步上了真机才是真正的考验。我们遇到的第一个运行期问题是“文字渲染正常但图标全消失”。排查下来发现是系统字体加载路径在鸿蒙端不一样有些自定义字体文件没有被正确加载。解决办法是把字体文件放到宿主工程的资源目录里明确指定字体路径而不是依赖默认查找。第二个典型问题是“权限弹窗弹了但功能没生效”。原因也很典型我们在代码里申请了权限也在配置文件里做了声明但没注意权限申请的时机必须在用户触发对应功能的前一刻。如果一进APP就申请权限用户往往会拒绝后面再想用就麻烦了。好的做法是用户第一次点“朗读”按钮时再弹窗请求“语音合成”权限用户此时明确知道“我按了朗读所以它在问我要权限”同意率会高很多。第三个问题是“界面被系统手势挡住”。演讲稿的编辑页有左右滑动手势但鸿蒙系统的侧边返回手势优先级更高结果用户本来想拖动编辑光标却被系统判定为返回操作直接退出页面。解决办法是把编辑区设计成按段落编辑避免在页面整体上做复杂的横向滑动同时也照顾了手掌误触的问题。5.3 鸿蒙特有适配细节清单鸿蒙设备在屏幕比例、系统导航习惯上和安卓、iOS有一些差异如果不专门适配用户会明显感觉“哪里不对劲”。我们整理过一张速查清单场景问题表现解决办法安全区域页面底部被系统导航条遮挡使用安全区域API适配内边距屏幕比例字体被拉伸或显示不全用动态文本缩放不用固定像素值横竖屏切换页面布局错乱锁定竖屏或按屏幕宽度重新计算布局返回手势与编辑手势冲突简化编辑交互不用长距离横向滑动状态栏颜色深浅色模式下文字看不清根据主题色动态调整状态栏图标样式分享面板文本内容为空异步回调后再读取分享结果这张表里的每一项都是在真机上一遍遍试出来的。说实话跨平台开发的“跨”字最难的不是把代码写到多端能跑而是让多端用户都觉得“这APP天生就该长在我这台设备上”。那些系统级的交互差异才是真正决定体验高低的地方。5.4 快速排错流程建议如果读者朋友也在做多端开发我建议从一开始就建立一套固定的排错流程能省下大量时间先看代码还是先查环境在多端开发里90%的诡异问题都是环境或依赖问题不是代码问题所以先跑一遍flutter doctor和flutter clean基本没坏处报错信息不一定准确。很多跨端框架把底层错误包装成了高层错误真正的根因藏在更底层所以看到报错别急着改代码先把完整错误日志翻到末尾几行往往能找到关键线索真机优先。模拟器上跑得通不代表真机没问题权限、性能、字体渲染这些差异只有真机能暴露保存每个稳定版本的构建产物。哪天改了代码把工程弄崩了还能回滚到能跑的版本这个习惯救了我们好几次。开发演讲稿生成器APP这个项目前后我们花了大概两个月。真正写业务代码的时间可能只占一半另一半全耗在了环境配置、多端适配、异步调试这些看似“不产生价值”但实际上决定成败的事情上。我个人的体会是跨平台开发一定要有一个心理预期多端的坑是躲不掉的但每踩一个坑后面所有同类项目都受益。最后再分享一个产品层面的小技巧演讲稿生成器这类工具预设模板远比算法参数重要。我们内置了“工作总结”“新人介绍”“产品宣讲”“活动致辞”几套不同口吻的模板用户即使不指定任何参数选一个模板点一下生成出来的稿子也已经有模有样。产品很多时候是“效果好”胜于“功能多”能把一个核心场景做透就比堆一堆花哨的按钮有价值得多。