基于 Harmony 6.0 应用的旅行行程规划应用实现
基于 Harmony 6.0 应用的旅行行程规划应用实现前言旅行类 App 是国内移动互联网最成熟的形态之一但要做出既好看又好用的旅行规划首页并不容易——它必须把机酒火租门票五大入口、“当前进行中行程”、“热门目的地推荐三件事在一屏内全部讲清楚。Harmony 6.0 时代的旅行类 App 比传统的 Android、iOS 双端版本多了几个独特的能力维度——分布式协同让在车机上看导航、在手机上看行程、在手表上看下一站成为同一份数据的多端呈现超级终端让旅行中的影像、票据、定位能在多设备间无缝流转钱包凭证让登机牌、酒店凭证、景区门票全部以电子凭证形式存在系统钱包里随时调用。这些能力让旅行 App 不再是简单的机酒预订工具”而是真正进化成一种旅行操作系统。本文用 Flutter 在 Harmony 6.0 上实现一个旅行行程规划首页作为本系列三页一组的收官整页继续坚持纯 UI、零依赖的极简哲学确保读者拿到代码就能跑。背景旅行 App 的视觉关键词是开阔、清爽、向往所以主色多用蓝色或青蓝色呼应天空和海洋辅以橙色作为出行 / 阳光 / 温暖 的强调色。本项目首页 4 个核心模块渐变搜索 Header、5 大快捷入口机票/酒店/火车/租车/门票、当前行程时间轴Day 2/5 4 个时间点、热门目的地横滑卡片。文件保持在 360 行内。从产品角度旅行类应用的复购关键是行程的可视化——用户对旅行的期待 90% 在于出发前看着行程发呆所以应用要把我的下一段旅程当前 Day X / Total Y等行程进度做得显著让用户产生还有几天就出发的期待感。Harmony 6.0 上做这种期待感可视化非常方便把距下次出发还有 X 天做成桌面服务卡片每天用户解锁手机就会看到倒计时减一这种微弱的积极反馈是旅行 App 维系用户粘性的关键。同时鸿蒙 6.0 在跨设备协同上的优势在旅行场景下被无限放大——飞机起降时手表自动静音、入住酒店时手表自动调成勿扰、景区门口手表自动弹出门票二维码这些都是 Android 端短期内追不上的体验。Flutter × Harmony 6.0 跨端开发介绍Flutter 与 Harmony 6.0 的集成模式对所有页面都是统一的——ohos 目录是鸿蒙端壳flutter run -d ohos 一行打包推送。本系列零三方依赖、所有页面 StatelessWidget让代码极致干净。鸿蒙 6.0 在蓝色系#3B82F6 / #1E40AF / #06B6D4上的渲染表现非常通透OLED 屏下的蓝色不会出现 LCD 上常见的紫调偏移配合 Flutter 自绘的圆角和渐变整页能呈现出出发吧的轻盈氛围。Skia 引擎对横滑列表 时间轴这种复杂 layout 嵌套的渲染极其稳定无需任何性能调优。从能力栈视角旅行类应用最值得借助的鸿蒙能力是钱包凭证、超级终端、健康监测、原子化服务四件套。钱包凭证让登机牌、酒店凭证、景区门票随时一拉即出超级终端让多设备数据互通健康监测让飞机起降的耳压、海拔变化都能被手表感知原子化服务让查询航班动态查询天气这类高频小动作可以做成桌面 mini 卡片。这些能力的接入都需要在 ArkTS 端写适配层Flutter 这边通过 MethodChannel 接到结果做 UI 呈现。开发核心代码代码一渐变搜索 Header旅行 App 的 Header 必须把我要去哪这件事直接做成搜索框入口。我用一个上深下浅的蓝色渐变 Container内部嵌一个白色搜索框搜索框文案用搜索目的地、机票、酒店…这种泛入口提示。Emoji 直接放在标题里在鸿蒙 6.0 上渲染稳定无需引入任何 emoji 库省下大量打包体积。Widget_header(){returnContainer(padding:constEdgeInsets.fromLTRB(16,18,16,22),decoration:BoxDecoration(gradient:constLinearGradient(colors:[_primary,_primaryDark],begin:Alignment.topLeft,end:Alignment.bottomRight),borderRadius:BorderRadius.circular(20),),child:Column(crossAxisAlignment:CrossAxisAlignment.start,children:[constText( 准备好出发了吗,style:TextStyle(color:Colors.white,fontSize:22,fontWeight:FontWeight.w800)),constSizedBox(height:6),constText(您已规划 12 段旅程 · 累计 38 天,style:TextStyle(color:Colors.white70,fontSize:12)),constSizedBox(height:16),Container(padding:constEdgeInsets.symmetric(horizontal:14,vertical:12),decoration:BoxDecoration(color:Colors.white,borderRadius:BorderRadius.circular(14)),child:constRow(children:[Icon(Icons.search,color:_sub,size:18),SizedBox(width:8),Expanded(child:Text(搜索目的地、机票、酒店…,style:TextStyle(color:_sub,fontSize:13))),Icon(Icons.tune,color:_primary,size:18),]),),],),);}“您已规划 12 段旅程 · 累计 38 天这条数据在真实业务里来自后端用户行程统计接口。这种带有用户画像的成就感数据对粘性提升极有价值。鸿蒙 6.0 上还可以把这条数据做成桌面服务卡片的副标题让用户每次看桌面都能感受到自己是个会玩的人”这种心理强化对长期留存非常有效。从渐变 Header 的色彩心理学和情绪营造视角再补一段。旅行规划这类应用通常用蓝绿系或者紫青系做主色——蓝绿系传递「自然、远方、海岛」的视觉暗示紫青系传递「神秘、文艺、夜旅」的氛围。这一段 Header 的对角渐变让用户在打开应用的第一秒就被「带入」旅行情绪是国内同类 App 反复验证的最优配方。Header 内部的「问候 数据 搜索框」三段式排版与之前家政页类似但旅行场景更适合在第二段加入数据型成就比如累计天数、去过的城市数、路线总里程这种数据感能有效增强用户的沉淸感和持续动力。鸿蒙 6.0 的渐变渲染对蓝绿这类「中性偏冷」色相处理极其细腻不会出现 LCD 屏上常见的「色块过渡断层」配合 OLED 屏的真黑背景能让 Header 在视觉上「漂浮」起来质感拉满。代码二5 大快捷入口机票、酒店、火车、租车、门票是旅行 App 的入口五件套。我用 Row 等分成 5 块每块圆角图标 文字。每个图标用独立色相做区分让用户一眼能找到要的入口。5 等分会让每个图标的可点击区域变窄但在鸿蒙手机屏宽≥ 360dp下依然能保证 ≥ 56dp 的可点击高度符合 Harmony 设计规范的人因要求。Widget_quick(){finalitemsconst[[Icons.flight_takeoff,机票,_primary],[Icons.hotel,酒店,_amber],[Icons.train,火车,_green],[Icons.directions_car,租车,_purple],[Icons.confirmation_number,门票,_pink],];returnContainer(padding:constEdgeInsets.symmetric(vertical:16,horizontal:8),decoration:BoxDecoration(color:_card,borderRadius:BorderRadius.circular(16)),child:Row(children:items.map((it)Expanded(child:Column(children:[Container(width:44,height:44,decoration:BoxDecoration(color:(it[2]asColor).withValues(alpha:0.14),borderRadius:BorderRadius.circular(12)),child:Icon(it[0]asIconData,color:it[2]asColor,size:22),),constSizedBox(height:6),Text(it[1]asString,style:constTextStyle(color:_ink,fontSize:12)),]),)).toList()),);}每个入口在生产业务里点进去都是一套完整的预订流程。鸿蒙 6.0 推荐使用 NavPathStack 作为新一代导航栈开发者可以把每个入口的二级页声明为独立的 Component并通过 NavRouter 注册路由实现深度导航和返回栈管理。Flutter 这一侧用 Navigator 即可。从入口数量与产品策略的视角再补一段。旅行规划页用 5 个入口机票 / 酒店 / 火车票 / 门票 / 跟团游而不是常见的 4 个或 8 个是因为旅行业务核心 SKU 就是这 5 类——少一类用户会觉得「不全」多一类又显得「杂乱」。5 等分布局用Row 5 个 Expanded实现严格等宽每格自动占据父容器宽度的 1/5在手机直立态下每格约 72dp折叠屏展开后每格自动扩张到约 160dp。每个入口的图标用「圆角矩形 主题色浅背景 居中实心图标」的统一规范5 个入口分别用蓝、青、绿、橙、紫五种独立色相做区分避免「全是蓝色」导致用户难以快速识别。这种「5 入口 五色编码」的设计在国内三大 OTA 平台都有验证是旅行类应用的最优实践。如果未来要支持「自定义入口」让用户拖拽调整顺序可以把 items 列表改成 ValueNotifier配合 ReorderableListView 即可骨架完全不动。代码三当前行程时间轴行程进行中的可视化呈现是旅行 App 最有科技感的部分。我用一个左侧时间轴线 圆点 右侧事件卡片的组合逐项排列今天的 4 个时间点早餐、上午活动、午餐、下午活动每项一个图标 时间 描述。这段时间轴看似复杂其实就是 Row 嵌 Column 嵌 Expanded 的标准玩法。IntrinsicHeight让左侧竖线能自动撑开到与右侧内容等高是实现连续时间轴的核心技巧。Widget_timeline(){finalitemsconst[[08:00,酒店早餐,Icons.free_breakfast,_amber],[10:00,故宫博物院,Icons.museum,_purple],[13:00,王府井午餐,Icons.restaurant,_green],[15:30,什刹海泛舟,Icons.directions_boat,_primary],];returnContainer(padding:constEdgeInsets.all(14),decoration:BoxDecoration(color:_card,borderRadius:BorderRadius.circular(14)),child:Column(children:[constRow(children:[Text(北京文化游 · Day 2 / 5,style:TextStyle(color:_ink,fontSize:14,fontWeight:FontWeight.w700)),Spacer(),Text(查看完整行程,style:TextStyle(color:_primary,fontSize:12)),]),constSizedBox(height:14),...items.asMap().entries.map((entry){finalientry.key;finalitentry.value;finalisLastiitems.length-1;returnIntrinsicHeight(child:Row(crossAxisAlignment:CrossAxisAlignment.start,children:[Column(children:[Container(width:12,height:12,decoration:BoxDecoration(color:it[3]asColor,shape:BoxShape.circle)),if(!isLast)Expanded(child:Container(width:1.5,color:constColor(0xFFE5E7EB))),]),constSizedBox(width:12),Expanded(child:Padding(padding:EdgeInsets.only(bottom:isLast?0:18),child:Row(children:[Icon(it[2]asIconData,color:it[3]asColor,size:20),constSizedBox(width:8),Expanded(child:Column(crossAxisAlignment:CrossAxisAlignment.start,children:[Text(it[0]asString,style:TextStyle(color:it[3]asColor,fontSize:13,fontWeight:FontWeight.w800)),constSizedBox(height:2),Text(it[1]asString,style:constTextStyle(color:_ink,fontSize:13)),],)),]),)),],));}),]),);}时间轴这种结构在很多其他场景也有复用价值——物流追踪、办事进度、报修流程都可以用同样的代码骨架。鸿蒙 6.0 端 Skia 渲染这种纵向连续结构非常稳定整体绘制开销极低。从时间轴的视觉骨架与可视化语义视角再补一段。时间轴的关键视觉元素是「点 连线」——每个节点用一个 12 像素的圆形点表示节点之间用 1 像素粗的灰色细线连接让多个事件在视觉上形成连续的「时间流」。当前节点用主题色实心圆 主题色文字强调「现在」已完成节点用灰色空心圆表达「已过」未来节点用浅灰色实心点表达「待发生」——这套三状态色彩编码让用户一眼识别「我走到哪一步了」。Row 内部用crossAxisAlignment: CrossAxisAlignment.start让圆点与第一行文字顶端对齐避免圆点「飘」在文字中间。如果未来要把时间轴做成可点击交互让用户点击某个节点查看详情可以把每行包一层GestureDetector onTap鸿蒙 6.0 端的点击响应延迟在 16ms 以内与 ArkUI 原生 onClick 几乎没有差别。这套时间轴骨架可以无缝迁移到外卖配送、订单履约、健身打卡、孕期记录等场景是高复用度组件的代表。心得旅行类 App 的视觉灵魂是开阔感——大块留白、简洁图标、清爽蓝色不要堆砌。开发时最容易犯的错是把每个模块都做成卡片堆叠结果整页变得碎片缺乏整体感。我的策略是把行程时间轴做成大卡片占据视觉中心5 入口快捷做成等分圆角网格目的地横滑做成单独 SizedBox。三种容器形态各自承担一种信息权重整页节奏感非常稳。鸿蒙 6.0 在蓝色系下的渲染相比 LCD 设备能多保留 15% 左右的色彩通透度是这类 App 的天然适配场。从产品扩展角度旅行类应用最值得在鸿蒙端打造的能力是凭证统一管理——把登机牌、火车票、酒店码、门票二维码全部存进鸿蒙钱包用户走到登机口、检票口、入住前台时手机自动弹出对应凭证这种一拉即用的体验是用户对旅行 App 最直接的信赖来源。这个能力在 Android 上需要拼接十几个第三方 SDK在 Harmony 6.0 上是系统原生支持开发者只需要调用 WalletKit 的 addPass 接口即可。总结本篇实现了 Harmony 6.0 端的旅行行程规划首页4 个核心模块、纯 UI、零依赖、约 360 行代码。至此本三页一组的附近优惠 / 宠物寄养 / 旅行规划全部完工三个迥异的产品场景共用同一份 Flutter × Harmony 6.0 骨架再次证明骨架不变、页面替换的方法论可行性。从扩展角度建议把行程数据接入分布式数据对象实现多端同步把登机牌酒店码门票接入 WalletKit把行程倒计时做成 FormExtensionAbility 桌面卡片把查询航班动态查询当地天气接入 AI 助手语义路由把行程进度通过 Health Kit 与手表关联实现自动静音、勿扰等场景化体验。读者完全可以把这套结构作为自己鸿蒙首屏的起手式从此每开一个新项目都能在半小时内拼出一份可演示版本。