1. 项目背景与核心需求
最近在开发一款基于OpenHarmony的剧本杀组队App,选择Flutter作为跨端框架主要是看中其高效的开发体验和良好的性能表现。剧本杀行业这两年发展迅猛,但线下组队效率低、信息不对称的问题一直存在。我们这款App要解决的核心痛点就是:让玩家快速找到附近合适的剧本杀店铺和场次,同时方便店铺展示特色剧本和组局信息。
技术选型上,Flutter for OpenHarmony这个组合可能很多人还不熟悉。简单来说,它让我们可以用Dart语言开发同时运行在OpenHarmony和其他平台的应用。相比原生开发,能节省至少30%的人力成本。这次要实现的店铺列表和详情页,是用户打开App最先接触的模块,直接关系到留存率。
2. 技术架构设计
2.1 整体技术栈
- 前端:Flutter 3.7 + Dart 2.19
- 跨端适配:OpenHarmony 3.2 Release
- 状态管理:Riverpod 2.3
- 网络请求:Dio 5.0
- 地图服务:高德地图Flutter插件
2.2 页面路由设计
采用基于GoRouter的导航方案,主要考虑两点:
- 需要支持深链接跳转(比如从分享链接直接打开某个店铺)
- 路由守卫需求(某些页面需要登录后才能访问)
路由配置示例:
dart复制final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const ShopListPage(),
routes: [
GoRoute(
path: 'detail/:shopId',
builder: (context, state) {
final shopId = state.pathParameters['shopId']!;
return ShopDetailPage(shopId: shopId);
},
),
],
),
],
);
3. 店铺列表页实现
3.1 数据获取与处理
店铺数据通过REST API获取,考虑到性能我们做了分页加载。这里有个关键点:如何高效合并新老数据。
dart复制class ShopRepository {
Future<PagedList<Shop>> getShops({
required int page,
required int pageSize,
required Location location,
}) async {
final response = await dio.get('/shops', queryParameters: {
'page': page,
'size': pageSize,
'lat': location.latitude,
'lng': location.longitude,
});
return PagedList.fromJson(
response.data,
(json) => Shop.fromJson(json),
);
}
}
3.2 UI组件设计
列表项采用Card布局,包含:
- 店铺封面图(使用cached_network_image优化加载)
- 基础信息(名称、评分、距离)
- 特色标签(恐怖本专精、DM颜值高等)
- 实时组队状态
dart复制ListView.builder(
itemCount: shops.length,
itemBuilder: (context, index) {
final shop = shops[index];
return ShopCard(
shop: shop,
onTap: () => context.push('/detail/${shop.id}'),
);
},
)
3.3 性能优化要点
- 使用ListView.builder而非Column+List,避免一次性渲染所有项
- 对图片实现预加载和缓存
- 分页加载阈值设为当前列表的1/3处
- 对距离计算等耗时操作使用Isolate
4. 店铺详情页实现
4.1 页面结构设计
详情页采用CustomScrollView实现复杂滚动效果,主要区块:
- 顶部画廊(展示店铺环境照片)
- 基本信息区
- 特色剧本列表(横向滑动)
- 近期场次表格
- 用户评价板块
4.2 关键交互实现
画廊自动轮播:
dart复制PageView.builder(
controller: _pageController,
itemCount: shop.images.length,
itemBuilder: (context, index) {
return CachedNetworkImage(
imageUrl: shop.images[index],
fit: BoxFit.cover,
);
},
);
// 自动轮播逻辑
Timer.periodic(const Duration(seconds: 3), (timer) {
if (_pageController.hasClients) {
final nextPage = (_pageController.page!.toInt() + 1) % shop.images.length;
_pageController.animateToPage(
nextPage,
duration: const Duration(milliseconds: 500),
curve: Curves.ease,
);
}
});
场次预约功能:
dart复制void _handleReserve(Session session) async {
final result = await showModalBottomSheet<bool>(
context: context,
builder: (ctx) => ReserveSheet(session: session),
);
if (result == true) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('预约成功')),
);
}
}
5. 跨平台适配要点
5.1 OpenHarmony特有配置
- 在
build.gradle中添加OHOS适配:
groovy复制ohos {
compileSdkVersion = 7
defaultConfig {
compatibleSdkVersion = 6
}
}
- 处理平台差异代码:
dart复制if (Platform.isOpenHarmony) {
// OHOS特有实现
} else {
// 其他平台实现
}
5.2 常见兼容性问题
- 字体渲染差异:需要在OHOS设备上测试字号和行高
- 动画性能:某些复杂动画在OHOS上需要降级处理
- 权限系统:地理位置等权限的申请方式不同
6. 调试与优化实录
6.1 性能问题排查
遇到列表卡顿问题时,通过Flutter Performance工具发现是店铺距离计算阻塞UI。解决方案:
- 将距离计算移到Isolate
- 对计算结果进行缓存
- 滚动时暂停计算
6.2 内存优化技巧
- 对画廊图片实现懒加载
- 离开详情页时释放大图资源
- 使用
ListView.separated替代连续Card减少Widget数量
6.3 实际测量数据
优化前后对比(测试设备:华为MatePad Pro):
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 列表滚动FPS | 42 | 58 |
| 详情页内存占用 | 187MB | 112MB |
| 冷启动时间 | 2.3s | 1.7s |
7. 业务逻辑扩展思考
7.1 智能推荐算法
后期可以基于用户行为数据实现:
- 基于位置的实时热门店铺推荐
- 根据玩家偏好匹配剧本类型
- 组队成功率预测
7.2 即时通讯集成
考虑使用Socket.io实现:
- 玩家与店铺客服沟通
- 组队成员间的聊天室
- 开本提醒通知
这个项目最让我意外的是Flutter在OpenHarmony上的运行效果比预期好很多,特别是在华为设备上的性能表现。不过也遇到一些坑,比如OHOS平台的键盘弹出行为与Android有差异,需要单独处理。建议大家在开发时尽早准备OHOS测试机,不要等到最后才做兼容性测试。