1. 项目背景与核心需求
剧本杀作为一种新兴的线下社交娱乐方式,近年来在国内迅速流行。玩家通过角色扮演和推理互动完成剧本任务,这种形式对组队灵活性要求极高。传统微信群组队方式存在信息杂乱、统计困难等问题,这正是我们开发专用组队App的市场切入点。
在Flutter for OpenHarmony技术栈的选择上,我们主要基于三点考量:
- 跨平台一致性:Flutter的Skia渲染引擎能保证在OpenHarmony和其他平台上的UI一致性
- 性能表现:Flutter的Dart语言AOT编译特性适合高频交互的表单场景
- 生态适配:华为提供的Flutter for OpenHarmony插件已覆盖常用设备能力
2. 表单架构设计
2.1 整体组件结构
采用MVC模式分层:
dart复制|- models/
| |- group_model.dart // 组队数据模型
|- views/
| |- form_page.dart // 主表单页面
| |- components/ // 表单子组件
| |- script_picker.dart
| |- shop_selector.dart
|- controllers/
|- form_controller.dart // 表单逻辑控制
2.2 关键交互设计
- 级联选择:剧本→店铺→时间的级联关系
- 实时验证:表单字段的联动校验
- 状态管理:使用Provider实现跨组件状态共享
3. 核心组件实现
3.1 剧本选择器实现
采用ChoiceChip+搜索的组合方案:
dart复制Wrap(
spacing: 8,
children: scripts.map((script) {
return ChoiceChip(
label: Text(script.name),
selected: _selectedScript == script,
onSelected: (selected) {
setState(() {
_selectedScript = selected ? script : null;
_selectedShop = null; // 重置店铺选择
});
},
);
}).toList(),
)
关键技巧:通过
Autocomplete组件实现剧本搜索功能,配合本地缓存减少网络请求
3.2 动态时间选择器
基于选定的店铺开放时间动态生成可选时段:
dart复制List<TimeSlot> generateTimeSlots(Shop shop) {
final slots = <TimeSlot>[];
DateTime now = DateTime.now();
DateTime start = DateTime(
now.year,
now.month,
now.day,
shop.openHour
);
// 生成30分钟间隔的时段
while (start.hour < shop.closeHour) {
slots.add(TimeSlot(
start: start,
end: start.add(Duration(minutes: 30))
));
start = start.add(Duration(minutes: 30));
}
return slots;
}
4. 复杂表单处理
4.1 联动验证逻辑
dart复制String? validateForm() {
if (_selectedScript == null) return '请选择剧本';
if (_selectedShop == null) return '请选择店铺';
if (_selectedTime == null) return '请选择时间';
if (_playerCount < 4) return '至少需要4人组队';
return null;
}
4.2 数据提交处理
使用Dio处理表单提交:
dart复制Future<void> submitForm() async {
final isValid = validateForm();
if (isValid != null) {
showSnackBar(isValid);
return;
}
try {
final response = await Dio().post(
'/api/groups',
data: GroupModel(
script: _selectedScript!,
shop: _selectedShop!,
time: _selectedTime!,
players: _playerCount,
price: _pricePerPerson
).toJson()
);
// 处理成功响应
} catch (e) {
// 错误处理
}
}
5. 性能优化实践
5.1 列表渲染优化
对长列表使用ListView.builder + AutomaticKeepAliveClientMixin:
dart复制class _ScriptList extends StatefulWidget {
@override
_ScriptListState createState() => _ScriptListState();
}
class _ScriptListState extends State<_ScriptList>
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context);
return ListView.builder(
itemCount: scripts.length,
itemBuilder: (ctx, index) => ScriptItem(scripts[index]),
);
}
}
5.2 状态管理优化
采用ProxyProvider实现级联更新:
dart复制MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ScriptProvider()),
ProxyProvider<ScriptProvider, ShopProvider>(
update: (_, scriptProvider, __) =>
ShopProvider(scriptProvider.selectedScript)
)
],
child: Consumer<ShopProvider>(
builder: (context, shopProvider, _) {
// 店铺选择器UI
}
)
)
6. 常见问题排查
6.1 ChoiceChip选中状态异常
现象:点击后选中状态不更新
解决方案:
- 确保父组件使用
StatefulWidget - 在
onSelected回调中调用setState - 检查
selected属性绑定是否正确
6.2 表单提交后数据残留
处理方法:
dart复制void clearForm() {
setState(() {
_selectedScript = null;
_selectedShop = null;
_selectedTime = null;
_playerCount = 4;
_pricePerPerson = 50;
_remarkController.clear();
});
}
7. 扩展功能实现
7.1 本地缓存策略
使用shared_preferences缓存剧本列表:
dart复制Future<void> loadScripts() async {
final prefs = await SharedPreferences.getInstance();
final cached = prefs.getString('scripts');
if (cached != null) {
setState(() {
scripts = Script.listFromJson(cached);
});
}
// 同时发起网络请求更新数据
fetchScripts();
}
7.2 表单草稿功能
实现自动保存草稿:
dart复制Timer? _saveTimer;
void onFormChanged() {
_saveTimer?.cancel();
_saveTimer = Timer(Duration(seconds: 3), () {
_saveDraft();
});
}
Future<void> _saveDraft() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('draft', jsonEncode({
'script': _selectedScript?.id,
'shop': _selectedShop?.id,
// 其他字段...
}));
}
在表单开发过程中,我发现OpenHarmony平台上的Flutter渲染性能表现优异,特别是在处理复杂表单动画时帧率稳定。一个实用的调试技巧是在flutter run时添加--profile参数,可以实时监控UI线程性能
