Flutter for OpenHarmony社团管理App实战:任务列表实现
本文介绍了如何使用Flutter实现一个社团任务管理页面。主要内容包括: 页面功能:按状态分类展示任务(待完成/进行中/已完成),显示优先级和截止时间,支持状态切换和新建任务。 技术实现: 使用TabBar实现状态分类导航 Provider进行状态管理 ListView展示任务卡片 优先级颜色标识(红/橙/绿) 逾期任务判断逻辑 浮动按钮添加新任务 关键组件: TabController管理标签页

社团日常运营中,任务管理是个刚需。活动策划要分工、物资采购要跟进、宣传推广要落实,这些都需要有人负责。今天我们就来实现一个任务列表页面,让社团的每件事都有人管、有人跟。
这个页面要做的事情:按状态分类展示任务、显示优先级和截止时间、支持状态切换和新建任务。
引入依赖
import 'package:flutter/material.dart';
Material库提供基础UI组件,每个页面都要引入。
没它啥也干不了。
import 'package:provider/provider.dart';
Provider做状态管理,任务数据从这里拿。
数据变了页面自动刷新。
import 'package:intl/intl.dart';
intl库用来格式化日期,显示截止时间要用到。
这个库功能挺强大的。
import '../../providers/app_provider.dart';
import '../../models/user.dart';
我们自己的Provider和数据模型。
Task模型定义在user.dart里。
页面基本结构
class TaskListPage extends StatefulWidget {
const TaskListPage({super.key});
State<TaskListPage> createState() => _TaskListPageState();
}
用StatefulWidget因为要管理TabController。
Tab切换需要状态。
状态类定义
class _TaskListPageState extends State<TaskListPage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
SingleTickerProviderStateMixin提供动画需要的vsync。
TabController需要这个。
初始化TabController
void initState() {
super.initState();
_tabController = TabController(
length: 3,
vsync: this
);
}
initState里创建TabController,length是Tab数量。
三个Tab:待完成、进行中、已完成。
销毁资源
void dispose() {
_tabController.dispose();
super.dispose();
}
dispose里销毁TabController,避免内存泄漏。
这是标准做法,别忘了。
构建页面
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('我的任务'),
bottom: TabBar(
controller: _tabController,
indicatorColor: Colors.white,
tabs: const [
Tab(text: '待完成'),
Tab(text: '进行中'),
Tab(text: '已完成'),
],
),
),
AppBar里嵌入TabBar,实现顶部标签导航。
indicatorColor设置指示器颜色。
TabBarView
body: Consumer<AppProvider>(
builder: (context, provider, _) {
return TabBarView(
controller: _tabController,
children: [
_buildTaskList(
provider.tasks.where(
(t) => t.status == '待完成'
).toList(),
provider
),
Consumer监听Provider,TabBarView和TabBar配合使用。
where过滤出对应状态的任务。
_buildTaskList(
provider.tasks.where(
(t) => t.status == '进行中'
).toList(),
provider
),
_buildTaskList(
provider.tasks.where(
(t) => t.status == '已完成'
).toList(),
provider
),
],
);
},
),
三个Tab分别显示不同状态的任务。
滑动或点击切换。
浮动按钮
floatingActionButton: FloatingActionButton(
backgroundColor: const Color(0xFF4A90E2),
child: const Icon(
Icons.add,
color: Colors.white
),
onPressed: () => _showAddTaskDialog(context),
),
);
}
右下角放个加号按钮,点击新建任务。
蓝色背景白色图标,很醒目。
任务列表构建
Widget _buildTaskList(
List<Task> tasks,
AppProvider provider
) {
if (tasks.isEmpty) {
return const Center(
child: Text(
'暂无任务',
style: TextStyle(color: Colors.grey)
)
);
}
空列表显示提示文字。
不能让用户看着空白发呆。
return ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: tasks.length,
itemBuilder: (context, index) {
final task = tasks[index];
return _buildTaskCard(task, provider);
},
);
}
ListView.builder渲染任务列表。
每个任务用卡片展示。
任务卡片
Widget _buildTaskCard(
Task task,
AppProvider provider
) {
Color priorityColor;
switch (task.priority) {
case 1:
priorityColor = Colors.red;
break;
case 2:
priorityColor = Colors.orange;
break;
default:
priorityColor = Colors.green;
}
根据优先级设置颜色,1是高优先级用红色。
2是中优先级用橙色,其他用绿色。
判断是否逾期
final isOverdue = task.deadline.isBefore(
DateTime.now()
) && task.status != '已完成';
截止时间在当前时间之前,且任务未完成,就是逾期。
逾期的任务要特殊标记。
卡片容器
return Card(
margin: const EdgeInsets.only(bottom: 12),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Card包裹卡片,Padding加内边距。
Column纵向排列内容。
优先级指示条
Container(
width: 4,
height: 40,
decoration: BoxDecoration(
color: priorityColor,
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(width: 12),
左边一个彩色竖条表示优先级。
红色高、橙色中、绿色低,一目了然。
任务标题
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
task.title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
decoration: task.status == '已完成'
? TextDecoration.lineThrough
: null,
)
),
任务标题用粗体,已完成的加删除线。
删除线表示这事儿已经办完了。
任务描述
if (task.description.isNotEmpty)
Text(
task.description,
style: const TextStyle(
color: Colors.grey,
fontSize: 13
)
),
],
),
),
描述不为空才显示,用灰色小字。
条件渲染避免空白。
状态切换菜单
PopupMenuButton<String>(
onSelected: (value) {
provider.updateTaskStatus(
task.id,
value
);
},
itemBuilder: (context) => [
const PopupMenuItem(
value: '待完成',
child: Text('待完成')
),
const PopupMenuItem(
value: '进行中',
child: Text('进行中')
),
const PopupMenuItem(
value: '已完成',
child: Text('已完成')
),
],
),
],
),
点击弹出菜单切换状态。
选择后调用provider更新。
截止时间显示
const SizedBox(height: 12),
Row(
children: [
Icon(
Icons.access_time,
size: 14,
color: isOverdue ? Colors.red : Colors.grey
),
const SizedBox(width: 4),
Text(
'截止: ${DateFormat('MM-dd HH:mm').format(task.deadline)}',
style: TextStyle(
fontSize: 12,
color: isOverdue ? Colors.red : Colors.grey
),
),
时间图标配合截止时间,逾期变红色。
DateFormat格式化成月日时分。
逾期标签
if (isOverdue) ...[
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2
),
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'已逾期',
style: TextStyle(
color: Colors.red,
fontSize: 10
)
),
),
],
逾期的任务显示红色标签。
提醒用户赶紧处理。
状态标签
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4
),
decoration: BoxDecoration(
color: task.status == '已完成'
? Colors.green.withOpacity(0.1)
: const Color(0xFF4A90E2).withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
task.status,
style: TextStyle(
color: task.status == '已完成'
? Colors.green
: const Color(0xFF4A90E2),
fontSize: 12
)
),
),
],
),
],
),
),
);
}
右下角显示当前状态,已完成用绿色,其他用蓝色。
Spacer把状态推到最右边。
新建任务对话框
void _showAddTaskDialog(BuildContext context) {
final titleController = TextEditingController();
final descController = TextEditingController();
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('新建任务'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: titleController,
decoration: const InputDecoration(
labelText: '任务标题',
border: OutlineInputBorder()
),
),
AlertDialog做弹窗,两个输入框。
mainAxisSize.min让高度自适应。
const SizedBox(height: 12),
TextField(
controller: descController,
decoration: const InputDecoration(
labelText: '任务描述',
border: OutlineInputBorder()
),
maxLines: 2,
),
],
),
描述输入框两行高度。
OutlineInputBorder加边框。
对话框按钮
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('取消')
),
ElevatedButton(
onPressed: () {
if (titleController.text.isNotEmpty) {
final provider = Provider.of<AppProvider>(
context,
listen: false
);
取消按钮关闭对话框。
创建按钮先验证标题不为空。
provider.addTask(Task(
clubId: 'club001',
title: titleController.text,
description: descController.text,
assignee: provider.currentUser.name,
deadline: DateTime.now().add(
const Duration(days: 7)
),
));
Navigator.pop(ctx);
}
},
child: const Text('创建'),
),
],
),
);
}
}
创建任务后关闭对话框。
默认截止时间一周后。
任务数据模型
class Task {
final String id;
final String clubId;
final String title;
final String description;
final String assignee;
final DateTime deadline;
final int priority;
final String status;
Task({
String? id,
required this.clubId,
required this.title,
this.description = '',
required this.assignee,
required this.deadline,
this.priority = 3,
this.status = '待完成',
}) : id = id ?? DateTime.now().millisecondsSinceEpoch.toString();
}
任务模型包含这些字段。
id默认用时间戳生成。
Provider中的任务方法
void addTask(Task task) {
_tasks.add(task);
notifyListeners();
}
void updateTaskStatus(String taskId, String status) {
final index = _tasks.indexWhere(
(t) => t.id == taskId
);
if (index != -1) {
_tasks[index] = Task(
id: _tasks[index].id,
clubId: _tasks[index].clubId,
title: _tasks[index].title,
description: _tasks[index].description,
assignee: _tasks[index].assignee,
deadline: _tasks[index].deadline,
priority: _tasks[index].priority,
status: status,
);
notifyListeners();
}
}
addTask添加任务,updateTaskStatus更新状态。
都要调用notifyListeners刷新UI。
测试数据
void initTestTasks(AppProvider provider) {
provider.addTask(Task(
clubId: 'club001',
title: '准备迎新活动物资',
description: '横幅、海报、小礼品',
assignee: '张三',
deadline: DateTime.now().add(
const Duration(days: 3)
),
priority: 1,
));
开发阶段用测试数据调试。
这个是高优先级任务。
provider.addTask(Task(
clubId: 'club001',
title: '联系场地负责人',
assignee: '李四',
deadline: DateTime.now().subtract(
const Duration(days: 1)
),
priority: 2,
status: '进行中',
));
}
这个任务已经逾期了。
页面上会显示红色警告。
小结
任务列表页面功能挺全的,Tab分类、优先级标识、逾期提醒、状态切换、新建任务都有了。代码量不小但结构清晰。
代码里用到的技巧:TabController管理标签页、PopupMenuButton做菜单、条件表达式控制样式。这些在其他场景也经常用到。
实际项目中可能还要加任务编辑、删除、指派这些功能,但基本框架就是这样。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)