flutter_for_openharmony手语学习app实战+每日任务实现
本文介绍了如何实现一个每日任务页面,包含三个核心模块:进度展示、任务列表和连续签到奖励。使用Provider状态管理获取全局数据,动态判断任务完成状态。页面采用纵向布局,顶部进度卡片显示圆形进度条和完成状态,中间任务列表使用图标和文字样式区分完成状态,并展示积分奖励。通过数据驱动和情感化设计增强用户体验,激励用户持续学习。

每日任务是激励用户持续学习的重要功能,通过设置小目标和奖励机制提升用户活跃度。本文介绍如何实现一个每日任务页面,包括进度展示、任务列表和连续签到奖励。
Provider状态管理
获取全局状态数据:
class DailyTaskScreen extends StatelessWidget {
const DailyTaskScreen({super.key});
Widget build(BuildContext context) {
final appProvider = Provider.of<AppProvider>(context);
final tasks = [
{'title': '完成1个基础课程', 'points': 10, 'completed': appProvider.todayLearned >= 1},
{'title': '完成3个课程', 'points': 30, 'completed': appProvider.todayLearned >= 3},
{'title': '完成5个课程', 'points': 50, 'completed': appProvider.todayLearned >= 5},
{'title': '完成一次测验', 'points': 20, 'completed': false},
{'title': '收藏一个课程', 'points': 5, 'completed': appProvider.favorites.isNotEmpty},
{'title': '学习10分钟', 'points': 15, 'completed': false},
];
使用Provider.of获取AppProvider实例,访问全局状态如今日学习数量、收藏列表等。任务完成状态根据Provider中的数据动态判断,比如todayLearned >= 1判断是否完成1个课程。这种数据驱动的方式让任务状态自动更新。
页面布局
构建整体的滚动布局:
return Scaffold(
appBar: AppBar(title: const Text('每日任务')),
body: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
children: [
_buildProgressCard(context, appProvider),
SizedBox(height: 16.h),
_buildTaskList(tasks),
SizedBox(height: 16.h),
_buildRewardSection(),
],
),
),
);
}
用SingleChildScrollView包裹,内容过多时可以滚动。Column纵向排列三个区域:进度卡片、任务列表和奖励区域。SizedBox控制区域间距,让布局疏密有致。这种分区设计让信息层次清晰。
进度卡片
构建顶部的进度展示:
Widget _buildProgressCard(BuildContext context, AppProvider appProvider) {
final completedTasks = appProvider.todayLearned;
final totalTasks = appProvider.dailyGoal;
return Card(
child: Padding(
padding: EdgeInsets.all(20.w),
child: Column(
children: [
CircularPercentIndicator(
radius: 60.r,
lineWidth: 10.w,
percent: (completedTasks / totalTasks).clamp(0.0, 1.0),
从Provider获取已完成和总目标数量,计算完成百分比。CircularPercentIndicator是第三方组件,显示圆形进度条。clamp(0.0, 1.0)限制百分比在0-1之间,避免超过100%或为负数。这种可视化展示比纯数字更直观。
进度条中心内容
显示完成数量:
center: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'$completedTasks',
style: TextStyle(fontSize: 28.sp, fontWeight: FontWeight.bold),
),
Text('/ $totalTasks', style: TextStyle(fontSize: 14.sp, color: Colors.grey)),
],
),
progressColor: const Color(0xFF00897B),
backgroundColor: Colors.grey[200]!,
),
SizedBox(height: 16.h),
进度条中心显示已完成数量和总目标,用大字号突出已完成数,小字号灰色显示总数。progressColor设为主题色,backgroundColor设为浅灰色。这种对比设计让进度一目了然。
进度描述
显示进度标题和鼓励文字:
Text(
'今日学习进度',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w500),
),
SizedBox(height: 8.h),
Text(
completedTasks >= totalTasks ? '🎉 太棒了!今日目标已完成' : '继续加油,还差${totalTasks - completedTasks}个任务',
style: TextStyle(fontSize: 14.sp, color: Colors.grey[600]),
),
],
),
),
);
}
标题用中等字号和半粗体,描述文字根据完成情况动态变化。完成目标显示庆祝emoji和鼓励文字,未完成显示剩余任务数。这种情感化设计增强了用户的成就感和动力。
任务列表
构建任务列表区域:
Widget _buildTaskList(List<Map<String, dynamic>> tasks) {
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.w),
child: Text(
'任务列表',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
),
),
任务列表用Card包裹,顶部显示"任务列表"标题。crossAxisAlignment.start让内容左对齐,符合阅读习惯。标题用粗体突出显示,与任务项形成视觉层次。
任务列表项
使用map生成任务项:
...tasks.map((task) => ListTile(
leading: Icon(
task['completed'] as bool ? Icons.check_circle : Icons.radio_button_unchecked,
color: task['completed'] as bool ? Colors.green : Colors.grey,
),
title: Text(
task['title'] as String,
style: TextStyle(
decoration: task['completed'] as bool ? TextDecoration.lineThrough : null,
color: task['completed'] as bool ? Colors.grey : Colors.black,
),
),
用...展开运算符将map结果插入列表。左侧图标根据完成状态显示:已完成显示绿色对勾,未完成显示灰色空心圆。标题文字已完成时添加删除线并变灰,未完成时正常显示。这种视觉反馈让用户清楚任务状态。
积分标签
右侧显示任务奖励:
trailing: Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration(
color: Colors.amber.withOpacity(0.2),
borderRadius: BorderRadius.circular(12.r),
),
child: Text(
'+${task['points']}积分',
style: TextStyle(fontSize: 12.sp, color: Colors.amber[800]),
),
),
)).toList(),
],
),
);
}
积分标签用金色半透明背景和深金色文字,圆角设计更柔和。金色代表奖励和价值,符合用户对积分的心理预期。+号强调这是获得的奖励,激励用户完成任务。
连续签到奖励
构建签到奖励区域:
Widget _buildRewardSection() {
return Card(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'连续签到奖励',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
),
SizedBox(height: 12.h),
签到奖励用Card包裹,标题用粗体显示。crossAxisAlignment.start让内容左对齐。这个区域展示连续签到的进度,鼓励用户每天打卡。
签到天数展示
使用List.generate生成7天:
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(7, (index) {
final isCompleted = index < 3;
return Column(
children: [
Container(
width: 36.w,
height: 36.w,
decoration: BoxDecoration(
color: isCompleted ? const Color(0xFF00897B) : Colors.grey[200],
shape: BoxShape.circle,
),
child: Icon(
isCompleted ? Icons.check : Icons.card_giftcard,
color: isCompleted ? Colors.white : Colors.grey,
size: 20.sp,
),
),
List.generate生成7个签到圆圈,mainAxisAlignment.spaceAround让它们均匀分布。已签到显示主题色背景和白色对勾,未签到显示灰色背景和礼物图标。这种进度可视化让用户清楚签到情况。
天数标签
显示第几天:
SizedBox(height: 4.h),
Text('第${index + 1}天', style: TextStyle(fontSize: 10.sp)),
],
);
}),
),
],
),
),
);
}
}
每个圆圈下方显示"第X天"文字,用小字号避免占用过多空间。index + 1将索引转换为天数(从1开始)。这种设计让用户知道每个圆圈代表第几天的签到。
CircularPercentIndicator的优势
使用第三方进度条组件:
CircularPercentIndicator(
radius: 60.r,
lineWidth: 10.w,
percent: (completedTasks / totalTasks).clamp(0.0, 1.0),
center: Column(...),
progressColor: const Color(0xFF00897B),
backgroundColor: Colors.grey[200]!,
)
CircularPercentIndicator提供圆形进度条功能,支持自定义半径、线宽、颜色和中心内容。自己实现需要使用CustomPaint绘制,代码复杂。使用现成组件可以快速开发,且效果专业。
clamp方法的作用
限制数值范围:
percent: (completedTasks / totalTasks).clamp(0.0, 1.0),
clamp(0.0, 1.0)确保百分比在0-1之间。如果completedTasks为0,结果是0;如果超过totalTasks,结果是1。这种边界处理避免了除零错误和超过100%的异常显示。
展开运算符
简化列表插入:
...tasks.map((task) => ListTile(...)).toList(),
...展开运算符将map返回的列表展开,插入到父列表中。这比先创建列表再用addAll添加更简洁。展开运算符是Dart的语法糖,让代码更优雅。
条件表达式
根据状态显示不同内容:
task['completed'] as bool ? Icons.check_circle : Icons.radio_button_unchecked
task['completed'] as bool ? TextDecoration.lineThrough : null
completedTasks >= totalTasks ? '🎉 太棒了!今日目标已完成' : '继续加油,还差${totalTasks - completedTasks}个任务'
三元运算符? :根据条件返回不同值,一行代码实现条件逻辑。这种简洁的写法比if-else更适合简单的条件判断,代码可读性高。
List.generate的应用
生成重复元素:
List.generate(7, (index) {
final isCompleted = index < 3;
return Column(...);
})
List.generate生成指定数量的元素,回调函数接收索引参数。这比手动创建7个元素更简洁,也便于修改数量。index < 3模拟前3天已签到,实际项目中应该从服务器获取真实数据。
颜色的语义化
不同颜色传达不同信息:
Colors.green, // 已完成/成功
Colors.grey, // 未完成/待处理
Colors.amber, // 积分/奖励
Color(0xFF00897B), // 主题色/进度
绿色代表完成和成功,灰色代表待处理,金色代表奖励,主题色代表进度。这种颜色语义化符合用户认知习惯,无需文字说明就能理解含义。
删除线的应用
标识已完成任务:
TextStyle(
decoration: task['completed'] as bool ? TextDecoration.lineThrough : null,
color: task['completed'] as bool ? Colors.grey : Colors.black,
)
已完成的任务添加删除线并变灰,视觉上表示"已划掉"。这是待办事项应用的经典设计,用户一看就懂。删除线配合灰色,双重视觉提示更明确。
Provider的响应式更新
状态变化自动更新UI:
final appProvider = Provider.of<AppProvider>(context);
{'title': '完成1个基础课程', 'points': 10, 'completed': appProvider.todayLearned >= 1},
任务完成状态依赖Provider中的数据,当todayLearned变化时,Provider.of会通知组件重建,任务状态自动更新。这种响应式编程让数据与UI保持同步,无需手动刷新。
响应式布局
使用flutter_screenutil适配屏幕:
radius: 60.r,
lineWidth: 10.w,
fontSize: 28.sp,
padding: EdgeInsets.all(16.w),
.r用于圆角半径,.w用于宽度和线宽,.sp用于字号。这些单位会根据屏幕尺寸自动缩放,确保在不同设备上比例一致。一套代码适配所有屏幕。
小结
每日任务页面通过圆形进度条直观展示学习进度,任务列表清晰标识完成状态和积分奖励。连续签到奖励鼓励用户每天打卡,情感化的文字提示增强用户动力。整体设计注重可视化和激励机制,有效提升用户活跃度。
任务完成检测
实时检测任务完成状态:
void _checkTaskCompletion() {
for (var task in tasks) {
if (task['type'] == 'learn_count') {
task['completed'] = appProvider.todayLearned >= task['target'];
} else if (task['type'] == 'quiz') {
task['completed'] = appProvider.todayQuizCompleted;
}
}
setState(() {});
}
根据不同任务类型检查完成条件,学习类任务检查学习数量,测验类任务检查是否完成测验。检测逻辑应该在数据变化时自动触发。
积分奖励发放
完成任务后发放积分:
void _claimReward(Map task) async {
if (!task['completed'] || task['claimed']) return;
final points = task['points'] as int;
await appProvider.addPoints(points);
task['claimed'] = true;
setState(() {});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('获得${points}积分!')),
);
}
检查任务是否完成且未领取,调用Provider增加积分,标记为已领取,显示提示信息。这种即时反馈增强用户成就感。
每日重置逻辑
每天零点重置任务状态:
void _checkDailyReset() async {
final prefs = await SharedPreferences.getInstance();
final lastDate = prefs.getString('lastTaskDate');
final today = DateTime.now().toString().substring(0, 10);
if (lastDate != today) {
await appProvider.resetDailyTasks();
await prefs.setString('lastTaskDate', today);
}
}
对比上次任务日期和今天日期,如果不同则重置任务。这个检查应该在应用启动时执行,确保任务状态正确。
连续签到计算
计算连续签到天数:
int _calculateStreakDays() {
final signInDates = appProvider.signInHistory;
if (signInDates.isEmpty) return 0;
int streak = 1;
for (int i = signInDates.length - 1; i > 0; i--) {
final diff = signInDates[i].difference(signInDates[i-1]).inDays;
if (diff == 1) {
streak++;
} else {
break;
}
}
return streak;
}
从最近的签到日期往前遍历,如果相邻两天相差1天则连续,否则中断。这个算法可以准确计算连续天数。
签到奖励规则
不同天数给予不同奖励:
int _getStreakReward(int days) {
if (days >= 30) return 500;
if (days >= 14) return 200;
if (days >= 7) return 100;
if (days >= 3) return 50;
return 10;
}
连续签到天数越多,奖励越丰厚。这种递增奖励机制激励用户长期坚持。
任务推送通知
定时提醒用户完成任务:
void _scheduleTaskReminder() async {
final notification = FlutterLocalNotificationsPlugin();
await notification.zonedSchedule(
0,
'每日任务提醒',
'还有任务未完成,快来领取积分吧!',
_nextInstanceOf20PM(),
const NotificationDetails(...),
uiLocalNotificationDateInterpretation: ...,
matchDateTimeComponents: DateTimeComponents.time,
);
}
使用本地通知在每天晚上8点提醒用户完成任务。推送通知可以有效提升用户活跃度。
任务数据统计
统计任务完成情况:
Map<String, int> _getTaskStatistics() {
return {
'totalTasks': tasks.length,
'completedTasks': tasks.where((t) => t['completed']).length,
'totalPoints': tasks.fold(0, (sum, t) => sum + (t['points'] as int)),
'earnedPoints': tasks
.where((t) => t['completed'])
.fold(0, (sum, t) => sum + (t['points'] as int)),
};
}
计算总任务数、已完成数、总积分和已获得积分。这些统计数据可以显示在页面顶部,让用户了解整体情况。
任务难度调整
根据用户水平动态调整任务难度:
List<Map> _generateDailyTasks(int userLevel) {
if (userLevel < 5) {
return [
{'title': '完成1个课程', 'target': 1, 'points': 10},
{'title': '学习5分钟', 'target': 5, 'points': 5},
];
} else {
return [
{'title': '完成5个课程', 'target': 5, 'points': 50},
{'title': '完成2次测验', 'target': 2, 'points': 40},
];
}
}
新手用户任务简单,高级用户任务更有挑战性。这种个性化任务提升用户体验。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)