Flutter for OpenHarmony剧本杀组队App实战:我的组队管理实现
摘要: 本文介绍了一个剧本杀组队App中的"我的组队"功能实现,采用Flutter框架开发。该页面通过顶部Tab切换展示用户发起的、参与的及历史组队记录,每个组队卡片显示剧本信息、时间、人数和状态。核心功能包括组队编辑、取消、退出等操作,使用GetX简化状态管理。页面布局采用Scaffold+TabBar结构,数据以Map形式存储组队信息,包括剧本名称、类型、时间等字段。Tab

引言
我的组队管理是剧本杀组队App中用户管理自己组队信息的核心功能模块。用户可以查看自己发起的组队、参与的组队以及历史组队记录,还可以对组队进行编辑、取消等操作。本篇文章将详细讲解如何实现一个功能完善的组队管理页面,包括多Tab切换、组队状态管理和操作功能。
我的组队页面采用顶部Tab切换不同类型组队的设计,分为"我发起的"、"我参与的"和"历史记录"三个标签页。每个组队卡片展示剧本信息、时间、人数和状态,并提供相应的操作按钮。
功能需求分析
核心功能模块
- 我发起的组队:显示用户发起的所有组队,包括进行中和已完成的
- 我参与的组队:显示用户参与的所有组队
- 历史记录:显示用户的历史组队记录
- 组队操作:编辑、取消、退出、删除等操作
- 状态管理:显示组队的当前状态(招募中、进行中、已完成等)
用户交互需求
- 用户可以快速切换不同类型的组队
- 用户可以查看组队的详细信息
- 用户可以对自己发起的组队进行编辑
- 用户可以取消或退出组队
- 用户可以查看历史组队记录
核心代码实现
第一部分:导入依赖与类定义
import 'package:flutter/material.dart';
import 'package:get/get.dart';
/// 我的组队管理页面 - 管理用户的所有组队信息
/// 支持查看发起的组队、参与的组队和历史记录
/// 提供组队编辑、取消、退出等操作功能
class MyTeamsPage extends StatefulWidget {
const MyTeamsPage({super.key});
State<MyTeamsPage> createState() => _MyTeamsPageState();
}
首先导入必要的Flutter和GetX包。我们使用GetX框架来简化状态管理和路由导航。MyTeamsPage是一个StatefulWidget,用于管理组队列表的状态。这个页面需要维护多个Tab的状态,因此选择使用StatefulWidget而不是StatelessWidget。
GetX是一个强大的状态管理框架,提供了简洁的API用于路由导航、状态管理和依赖注入。通过使用GetX,我们可以减少样板代码,提高开发效率。
class _MyTeamsPageState extends State<MyTeamsPage>
with SingleTickerProviderStateMixin {
/// 主题色 - 紫色
static const Color _primaryColor = Color(0xFF6B4EFF);
_MyTeamsPageState是MyTeamsPage的State类,负责管理页面的状态和生命周期。SingleTickerProviderStateMixin提供了对TabController的支持,使得Tab切换时能够产生平滑的动画效果。
主题色定义为紫色(0xFF6B4EFF),这是整个应用的主色调。使用static const修饰符定义常量颜色,这样可以在整个类中复用,避免硬编码颜色值。
/// Tab控制器
late TabController _tabController;
/// 当前选中的Tab索引
int _currentTabIndex = 0;
/// 我发起的组队列表
final List<Map<String, dynamic>> _initiatedTeams = [
{
'id': '1',
'script': '年轮',
'type': '情感本',
'time': '今天 19:00',
'current': 4,
'total': 6,
'status': '招募中',
'store': '迷雾剧本杀',
},
{
'id': '2',
'script': '谍影重重',
'type': '推理本',
'time': '明天 14:00',
'current': 5,
'total': 6,
'status': '招募中',
'store': '剧本杀工坊',
},
];
TabController用于管理三个Tab的切换。_currentTabIndex记录当前选中的Tab索引,用于在Tab切换时更新UI。这个变量在Tab切换时会被更新,触发页面重建。
_initiatedTeams列表存储用户发起的所有组队信息。每个组队是一个Map对象,包含id、剧本名称、剧本类型、组队时间、当前人数、总人数、组队状态和店铺名称等关键信息。
使用Map而不是自定义类的好处是灵活性高,可以快速添加或修改字段。但在大型项目中,建议使用自定义的数据模型类来提高类型安全性和代码可维护性。
/// 我参与的组队列表
final List<Map<String, dynamic>> _participatedTeams = [
{
'id': '3',
'script': '密室逃脱',
'type': '悬疑本',
'time': '后天 20:00',
'current': 6,
'total': 6,
'status': '进行中',
'store': '谜题工坊',
'host': '张三',
},
];
/// 历史组队列表
final List<Map<String, dynamic>> _historyTeams = [
{
'id': '4',
'script': '古墓迷踪',
'type': '冒险本',
'time': '上周 19:00',
'current': 6,
'total': 6,
'status': '已完成',
'store': '冒险岛',
'rating': 4.5,
},
];
_participatedTeams列表存储用户参与的组队信息。与发起的组队不同,参与的组队包含组队发起者的信息(host字段),这样用户可以快速了解谁发起了这个组队。
_historyTeams列表存储用户的历史组队记录。历史组队包含评分信息(rating字段),用户可以查看之前参与的组队的评分。这个评分通常是由其他参与者给出的,反映了这个组队的质量。
这三个列表都是final修饰的,表示列表本身不会被重新赋值,但列表中的元素可以被修改。在实际项目中,这些数据应该从后端API获取,而不是硬编码在代码中。
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
_tabController.addListener(_handleTabChange);
}
/// 处理Tab切换
void _handleTabChange() {
setState(() {
_currentTabIndex = _tabController.index;
});
}
void dispose() {
_tabController.dispose();
super.dispose();
}
在initState中初始化TabController,指定有3个Tab。vsync参数设置为this,表示使用当前State作为TickerProvider。TickerProvider是Flutter中用于管理动画帧的接口,确保动画与屏幕刷新率同步。
添加监听器来处理Tab切换事件。当用户切换Tab时,_handleTabChange方法会被调用,更新_currentTabIndex并触发UI重建。setState方法会通知Flutter框架状态已改变,需要重新构建UI。
在dispose方法中释放TabController资源,防止内存泄漏。这是Flutter中的最佳实践。当页面被销毁时,必须释放所有占用的资源,包括控制器、流订阅等。
第二部分:页面主体结构
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('我的组队'),
centerTitle: true,
elevation: 0,
backgroundColor: _primaryColor,
foregroundColor: Colors.white,
bottom: TabBar(
controller: _tabController,
indicatorColor: Colors.white,
indicatorWeight: 3,
labelColor: Colors.white,
unselectedLabelColor: Colors.white70,
tabs: const [
Tab(text: '我发起的'),
Tab(text: '我参与的'),
Tab(text: '历史记录'),
],
),
),
backgroundColor: const Color(0xFFF5F5F5),
body: TabBarView(
controller: _tabController,
children: [
_buildInitiatedTeamsView(),
_buildParticipatedTeamsView(),
_buildHistoryTeamsView(),
],
),
);
}
build方法是页面的主入口。Scaffold提供了基本的Material Design布局结构,包括AppBar、body等主要组件。AppBar设置了页面标题"我的组队"和背景色,elevation设置为0表示不显示阴影。
TabBar包含三个Tab标签,分别对应"我发起的"、"我参与的"和"历史记录"三个功能模块。indicatorColor设置为白色,indicatorWeight设置为3表示指示器的厚度。labelColor和unselectedLabelColor分别设置选中和未选中Tab的文字颜色。
TabBarView与TabBar关联,当用户切换Tab时,会显示对应的视图内容。三个子视图分别通过_buildInitiatedTeamsView()、_buildParticipatedTeamsView()和_buildHistoryTeamsView()方法构建。
/// 构建我发起的组队视图
Widget _buildInitiatedTeamsView() {
if (_initiatedTeams.isEmpty) {
return _buildEmptyState(
icon: Icons.group_add_outlined,
title: '还没有发起过组队',
subtitle: '点击下方按钮发起一个新的组队吧',
buttonText: '发起组队',
onButtonTap: () => _navigateToCreateTeam(),
);
}
return ListView.builder(
padding: const EdgeInsets.all(12),
itemCount: _initiatedTeams.length,
itemBuilder: (context, index) {
return _buildTeamCard(
team: _initiatedTeams[index],
isInitiated: true,
onEdit: () => _editTeam(_initiatedTeams[index]),
onCancel: () => _cancelTeam(_initiatedTeams[index]),
onDelete: () => _deleteTeam(_initiatedTeams[index]),
);
},
);
}
_buildInitiatedTeamsView方法构建用户发起的组队列表视图。首先检查列表是否为空,如果为空则显示空状态提示,引导用户发起新的组队。
如果列表不为空,使用ListView.builder构建可滚动的列表。ListView.builder是高效的列表构建方式,只会渲染可见的项目,这对于大列表来说性能更好。
每个列表项通过_buildTeamCard方法构建,并传入相应的回调函数处理编辑、取消等操作。isInitiated参数设置为true,表示这是用户发起的组队,会显示不同的操作按钮。
/// 构建我参与的组队视图
Widget _buildParticipatedTeamsView() {
if (_participatedTeams.isEmpty) {
return _buildEmptyState(
icon: Icons.search_outlined,
title: '还没有参与过组队',
subtitle: '去组队大厅找一个感兴趣的组队吧',
buttonText: '浏览组队',
onButtonTap: () => _navigateToTeamHall(),
);
}
return ListView.builder(
padding: const EdgeInsets.all(12),
itemCount: _participatedTeams.length,
itemBuilder: (context, index) {
return _buildTeamCard(
team: _participatedTeams[index],
isInitiated: false,
onQuit: () => _quitTeam(_participatedTeams[index]),
onContact: () => _contactHost(_participatedTeams[index]),
);
},
);
}
_buildParticipatedTeamsView方法构建用户参与的组队列表视图。与发起的组队不同,参与的组队提供"联系车主"和"退出"两个操作按钮。isInitiated参数设置为false,表示这是用户参与的组队。
/// 构建历史记录视图
Widget _buildHistoryTeamsView() {
if (_historyTeams.isEmpty) {
return _buildEmptyState(
icon: Icons.history_outlined,
title: '还没有历史记录',
subtitle: '参与组队后会显示在这里',
buttonText: '返回首页',
onButtonTap: () => Get.back(),
);
}
return ListView.builder(
padding: const EdgeInsets.all(12),
itemCount: _historyTeams.length,
itemBuilder: (context, index) {
return _buildHistoryTeamCard(_historyTeams[index]);
},
);
}
_buildHistoryTeamsView方法构建历史组队记录视图。历史组队使用不同的卡片样式_buildHistoryTeamCard来展示,包含评分信息和完成状态。
这三个视图方法都遵循相同的模式:先检查列表是否为空,然后使用ListView.builder构建列表。这样的设计使得代码结构清晰,易于维护和扩展。每个视图都有对应的空状态提示,提升了用户体验。
第三部分:组队卡片组件
/// 构建组队卡片
Widget _buildTeamCard({
required Map<String, dynamic> team,
required bool isInitiated,
VoidCallback? onEdit,
VoidCallback? onCancel,
VoidCallback? onDelete,
VoidCallback? onQuit,
VoidCallback? onContact,
}) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
_buildTeamCard方法构建组队卡片的主体部分。卡片使用Container包装,设置了圆角(borderRadius: BorderRadius.circular(12))和阴影效果(boxShadow),使其看起来更加立体和现代。
第四部分:事件处理方法
/// 获取状态对应的颜色
Color _getStatusColor(String status) {
switch (status) {
case '招募中':
return Colors.blue;
case '进行中':
return Colors.orange;
case '已完成':
return Colors.green;
default:
return Colors.grey;
}
}
/// 编辑组队
void _editTeam(Map<String, dynamic> team) {
Get.snackbar('提示', '编辑组队:${team['script']}');
}
/// 取消组队
void _cancelTeam(Map<String, dynamic> team) {
Get.dialog(
AlertDialog(
title: const Text('取消组队'),
content: Text('确定要取消组队"${team['script']}"吗?'),
actions: [
TextButton(
onPressed: () => Get.back(),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () {
Get.back();
Get.snackbar('成功', '组队已取消');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
),
child: const Text(
'确定',
style: TextStyle(color: Colors.white),
),
),
],
),
);
}
_getStatusColor方法根据组队状态返回对应的颜色。这样的设计使得状态颜色的管理集中在一个地方,便于维护和修改。使用switch语句判断状态,返回对应的颜色。招募中使用蓝色,进行中使用橙色,已完成使用绿色,其他状态使用灰色。
_editTeam方法处理编辑组队的操作。实际项目中应该导航到编辑页面,这里使用SnackBar作为演示。SnackBar是一个临时的提示消息,会在屏幕底部显示,然后自动消失。
/// 删除组队
void _deleteTeam(Map<String, dynamic> team) {
Get.snackbar('提示', '删除组队:${team['script']}');
}
/// 退出组队
void _quitTeam(Map<String, dynamic> team) {
Get.dialog(
AlertDialog(
title: const Text('退出组队'),
content: Text('确定要退出组队"${team['script']}"吗?'),
actions: [
TextButton(
onPressed: () => Get.back(),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () {
Get.back();
Get.snackbar('成功', '已退出组队');
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
),
child: const Text(
'确定',
style: TextStyle(color: Colors.white),
),
),
],
),
);
}
_cancelTeam和_quitTeam方法都使用AlertDialog显示确认对话框。这样的设计能够防止用户误操作,提高应用的安全性。AlertDialog包含标题、内容和操作按钮,用户需要确认才能执行操作。
_contactHost方法用于与组队发起者联系。实际项目中应该导航到聊天页面或打开聊天窗口。这个方法可以集成消息系统,让用户能够直接与组队发起者沟通。
/// 联系车主
void _contactHost(Map<String, dynamic> team) {
Get.snackbar('提示', '跳转到聊天页面');
}
/// 导航到发起组队页面
void _navigateToCreateTeam() {
Get.snackbar('提示', '跳转到发起组队页面');
}
/// 导航到组队大厅
void _navigateToTeamHall() {
Get.snackbar('提示', '跳转到组队大厅');
}
}
_navigateToCreateTeam和_navigateToTeamHall方法用于页面导航。使用GetX框架的Get.snackbar进行提示,实际项目中应该使用Get.to()进行页面导航。GetX提供了简洁的API用于页面跳转和参数传递。
数据模型与业务逻辑
在实际项目中,应该定义清晰的数据模型。基础Team类定义了所有组队共有的属性,包括id、剧本名称、类型、状态、时间、人数和店铺名称。canEdit()方法检查是否可编辑,canCancel()方法检查是否可取消,getRemainingPlayers()方法计算剩余人数。
这样的设计使得数据结构清晰,便于维护和扩展。每个属性都有明确的含义,代码的可读性更高。
不同类型的组队可以继承Team类并添加特定属性。InitiatedTeam添加memberIds用于管理成员,ParticipatedTeam添加hostName和hostId用于存储发起者信息,HistoryTeam添加rating和review用于存储评分和评价。
这样的继承设计使得代码更加模块化,每个类只负责自己的属性和方法。当需要添加新的组队类型时,只需要继承Team类并添加新的属性即可。
状态管理与业务逻辑
组队的状态管理是核心功能。使用枚举定义所有可能的状态(招募中、进行中、已完成、已取消),每个状态对应不同的颜色和可执行的操作。
这样的设计使得状态管理更加清晰,避免了使用字符串导致的错误。枚举提供了类型安全,编译器可以检查状态值是否有效。
业务逻辑应该封装在服务类中。TeamService类提供editTeam()、cancelTeam()、quitTeam()等方法处理各种操作,同时提供getInitiatedTeams()、getParticipatedTeams()、getHistoryTeams()等方法获取不同类型的组队列表。这样的设计将业务逻辑与UI层分离,提高代码的可维护性。
服务类可以与后端API通信,获取和更新组队数据。这样的设计使得应用更加灵活,可以轻松切换数据源。
总结
通过本篇文章的学习,我们完成了我的组队管理页面的实现。这个页面提供了完整的组队生命周期管理功能,用户可以查看和管理自己的所有组队信息。
页面设计遵循了清晰的Tab切换、直观的卡片布局和友好的交互方式,为用户提供了良好的组队管理体验。在实际项目中,应该根据具体需求进一步完善这个功能,添加更多的业务逻辑和用户交互。
在后续的开发中,你可以根据实际需求进一步优化这个页面,添加更多的功能和交互。例如,可以添加下拉刷新、上拉加载更多、搜索和筛选等功能。这些功能可以进一步提升用户体验。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)