欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net




1. 项目介绍
在快节奏的现代生活中,高效的时间管理变得越来越重要。番茄工作法(Pomodoro Technique)作为一种流行的时间管理方法,通过将工作时间分解为25分钟的工作段和5分钟的休息段,帮助人们提高专注力和工作效率。本文将详细介绍如何使用 Flutter 实现一个功能完整的番茄时间管理应用,包括倒计时功能、任务管理、统计分析和自定义设置。
1.1 项目目标
- 实现一个功能完整的番茄时间管理应用
- 支持标准的番茄工作法时间设置
- 提供任务管理功能,帮助用户跟踪待办事项
- 实现统计功能,展示用户的工作和休息时间
- 支持自定义时间设置,适应不同用户的需求
- 确保在不同平台上的一致性表现
1.2 技术栈
- Flutter:跨平台 UI 框架
- Dart:编程语言
- Timer:用于实现倒计时功能
- StatefulWidget:用于管理应用状态
- AnimatedContainer:用于实现平滑过渡效果
- TextField:用于任务输入
- ListView:用于任务列表展示
2. 核心功能设计
2.1 番茄钟功能
- 工作时间:默认25分钟,可自定义
- 短休息:默认5分钟,可自定义
- 长休息:默认15分钟,可自定义
- 自动循环:每4个番茄后自动进入长休息
- 状态管理:工作、短休息、长休息、暂停、停止
2.2 任务管理
- 添加任务:通过文本输入添加待办事项
- 删除任务:支持删除已添加的任务
- 任务列表:以列表形式展示所有任务
2.3 统计功能
- 完成的番茄数:记录用户完成的番茄数量
- 工作时间:计算总工作时间
- 休息时间:计算总休息时间
2.4 设置功能
- 工作时间调整:可调整工作时间(5-60分钟)
- 短休息时间调整:可调整短休息时间(1-10分钟)
- 长休息时间调整:可调整长休息时间(5-30分钟)
3. 技术架构
3.1 项目结构
lib/
└── main.dart # 主应用文件,包含所有代码
3.2 组件结构
PomodoroApp
└── PomodoroScreen
├── State management (currentState, remainingTime, completedPomodoros, etc.)
├── Timer functionality (start, pause, stop, complete)
├── Task management (add, remove)
├── UI components
│ ├── Timer display
│ ├── Control buttons
│ ├── Statistics section
│ ├── Task management section
│ └── Settings section
└── Settings (workDuration, shortBreakDuration, longBreakDuration)
4. 关键代码解析
4.1 状态管理
dart
enum TimerState {
work,
shortBreak,
longBreak,
paused,
stopped,
}
class _PomodoroScreenState extends State<PomodoroScreen> {
TimerState _currentState = TimerState.stopped;
int _workDuration = 25 * 60; // 25分钟
int _shortBreakDuration = 5 * 60; // 5分钟
int _longBreakDuration = 15 * 60; // 15分钟
int _remainingTime = 25 * 60;
int _completedPomodoros = 0;
int _currentCycle = 0;
Timer? _timer;
List<String> _tasks = [];
String _newTask = '';
// ...
}
代码解析:
TimerState枚举:定义了计时器的不同状态_currentState:当前计时器状态_workDuration、_shortBreakDuration、_longBreakDuration:各阶段的持续时间_remainingTime:剩余时间(秒)_completedPomodoros:完成的番茄数量_currentCycle:当前循环次数_timer:计时器实例_tasks:任务列表_newTask:新任务输入
4.2 计时器功能
dart
void _startTimer() {
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
if (_remainingTime > 0) {
_remainingTime--;
} else {
_timer!.cancel();
_completeTimer();
}
});
});
setState(() {
if (_currentState == TimerState.stopped) {
_currentState = TimerState.work;
_remainingTime = _workDuration;
} else if (_currentState == TimerState.paused) {
_currentState = _currentState;
}
});
}
void _completeTimer() {
setState(() {
if (_currentState == TimerState.work) {
_completedPomodoros++;
_currentCycle++;
if (_currentCycle % 4 == 0) {
_currentState = TimerState.longBreak;
_remainingTime = _longBreakDuration;
} else {
_currentState = TimerState.shortBreak;
_remainingTime = _shortBreakDuration;
}
} else {
_currentState = TimerState.work;
_remainingTime = _workDuration;
}
_startTimer();
});
}
代码解析:
_startTimer方法:启动计时器,设置定时器每秒减少剩余时间_completeTimer方法:处理计时器完成后的逻辑,包括状态切换和循环计数- 自动循环逻辑:每4个番茄后自动进入长休息
4.3 任务管理
dart
void _addTask() {
if (_newTask.isNotEmpty) {
setState(() {
_tasks.add(_newTask);
_newTask = '';
});
}
}
void _removeTask(int index) {
setState(() {
_tasks.removeAt(index);
});
}
代码解析:
_addTask方法:添加新任务到任务列表_removeTask方法:从任务列表中删除指定任务
4.4 时间格式化
dart
String _formatTime(int seconds) {
int minutes = seconds ~/ 60;
int remainingSeconds = seconds % 60;
return '${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}';
}
代码解析:
_formatTime方法:将秒数格式化为 MM:SS 格式的字符串
5. 技术亮点与创新
5.1 状态管理
- 清晰的状态定义:使用枚举定义了计时器的不同状态,使代码结构更加清晰
- 状态切换逻辑:实现了完整的状态切换逻辑,包括工作、休息、暂停和停止
- 自动循环:实现了番茄工作法的自动循环功能,每4个番茄后自动进入长休息
5.2 用户界面
- 视觉反馈:通过颜色变化提供清晰的视觉反馈,工作状态为红色,休息状态为绿色
- 响应式布局 :使用
SingleChildScrollView确保在不同屏幕尺寸上的良好表现 - 卡片式设计:使用卡片式布局,使界面层次分明,易于理解
5.3 功能完整性
- 完整的番茄工作法实现:支持标准的番茄工作法时间设置和自动循环
- 任务管理:集成了任务管理功能,帮助用户跟踪待办事项
- 统计功能:提供了详细的统计信息,帮助用户了解自己的工作和休息时间
- 自定义设置:支持自定义时间设置,适应不同用户的需求
5.4 跨平台兼容性
- Flutter 跨平台:使用 Flutter 框架,确保在 Android、iOS、Web 等平台上的一致性表现
- 自适应设计:界面布局采用相对单位,适应不同屏幕尺寸
6. 应用场景与扩展
6.1 应用场景
- 工作学习:帮助用户在工作和学习中保持专注,提高效率
- 时间管理:帮助用户养成良好的时间管理习惯
- 任务跟踪:结合任务管理功能,帮助用户跟踪和完成待办事项
- 团队协作:在团队环境中,帮助团队成员保持专注和高效
6.2 扩展方向
- 数据持久化:添加本地存储功能,保存用户的任务和设置
- 云端同步:支持将数据同步到云端,实现跨设备同步
- 通知提醒:添加系统通知,在计时器完成时提醒用户
- 数据分析:提供更详细的数据分析,帮助用户了解自己的时间使用情况
- 主题切换:支持不同主题风格,满足用户的个性化需求
- 多语言支持:添加多语言支持,扩大应用的适用范围
7. 代码优化建议
7.1 性能优化
- 计时器优化 :使用
Timer.periodic时,确保在不需要时及时取消,避免内存泄漏 - 状态管理优化 :对于更复杂的应用,可以使用
Provider、Riverpod等状态管理库 - UI 优化 :使用
RepaintBoundary包裹复杂的 UI 组件,减少不必要的重绘
7.2 代码结构优化
- 组件化:将 UI 组件拆分为更小的、可复用的组件
- 逻辑分离:将业务逻辑与 UI 逻辑分离,提高代码的可维护性
- 参数化:将时间设置、颜色等参数提取为可配置的常量
7.3 用户体验优化
- 触觉反馈:在支持的设备上,添加触觉反馈,增强交互体验
- 动画效果:添加更多的动画效果,使界面更加生动
- 错误处理:添加适当的错误处理,提高应用的稳定性
- 用户引导:添加首次使用引导,帮助用户了解应用的使用方法
8. 测试与调试
8.1 测试策略
- 功能测试:测试计时器、任务管理、统计等核心功能
- 性能测试:测试应用在不同设备上的性能表现
- 兼容性测试:测试在不同平台、不同屏幕尺寸上的表现
- 用户体验测试:测试应用的易用性和用户体验
8.2 调试技巧
- 使用 Flutter DevTools:利用 Flutter DevTools 分析性能瓶颈和调试问题
- 添加日志:在关键位置添加日志,便于调试
- 使用模拟器:在不同尺寸的模拟器上测试,确保适配性
- 用户测试:邀请用户测试,收集反馈,不断改进
9. 总结与展望
9.1 项目总结
本项目成功实现了一个功能完整的番茄时间管理应用,主要功能包括:
- 番茄钟计时器:支持工作时间、短休息和长休息
- 自动循环:每4个番茄后自动进入长休息
- 任务管理:添加、删除任务
- 统计功能:显示完成的番茄数量、工作时间和休息时间
- 自定义设置:可调整工作时间、短休息时间和长休息时间
- 跨平台兼容:在 Android、iOS、Web 等平台上表现一致
9.2 技术价值
- 学习价值:展示了如何使用 Flutter 实现一个完整的时间管理应用
- 实用价值:提供了一个可直接使用的番茄时间管理工具
- 参考价值:为类似功能的开发提供了参考方案
9.3 未来展望
- 智能推荐:基于用户的使用习惯,智能推荐最佳的工作和休息时间
- 集成日历:与日历应用集成,帮助用户规划时间
- 团队功能:添加团队协作功能,帮助团队成员协调工作
- 健康监测:结合健康数据,提醒用户适当休息,保护身体健康
- AI 助手:添加 AI 助手,帮助用户更好地管理时间和任务
10. 附录
10.1 完整代码
dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
enum TimerState {
work,
shortBreak,
longBreak,
paused,
stopped,
}
void main() {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
));
runApp(const PomodoroApp());
}
class PomodoroApp extends StatelessWidget {
const PomodoroApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '番茄时间管理',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.red,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const PomodoroScreen(),
);
}
}
class PomodoroScreen extends StatefulWidget {
const PomodoroScreen({Key? key}) : super(key: key);
@override
State<PomodoroScreen> createState() => _PomodoroScreenState();
}
class _PomodoroScreenState extends State<PomodoroScreen> {
TimerState _currentState = TimerState.stopped;
int _workDuration = 25 * 60; // 25分钟
int _shortBreakDuration = 5 * 60; // 5分钟
int _longBreakDuration = 15 * 60; // 15分钟
int _remainingTime = 25 * 60;
int _completedPomodoros = 0;
int _currentCycle = 0;
Timer? _timer;
List<String> _tasks = [];
String _newTask = '';
void _startTimer() {
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
if (_remainingTime > 0) {
_remainingTime--;
} else {
_timer!.cancel();
_completeTimer();
}
});
});
setState(() {
if (_currentState == TimerState.stopped) {
_currentState = TimerState.work;
_remainingTime = _workDuration;
} else if (_currentState == TimerState.paused) {
_currentState = _currentState;
}
});
}
void _pauseTimer() {
if (_timer != null) {
_timer!.cancel();
}
setState(() {
_currentState = TimerState.paused;
});
}
void _stopTimer() {
if (_timer != null) {
_timer!.cancel();
}
setState(() {
_currentState = TimerState.stopped;
_remainingTime = _workDuration;
_currentCycle = 0;
});
}
void _completeTimer() {
setState(() {
if (_currentState == TimerState.work) {
_completedPomodoros++;
_currentCycle++;
if (_currentCycle % 4 == 0) {
_currentState = TimerState.longBreak;
_remainingTime = _longBreakDuration;
} else {
_currentState = TimerState.shortBreak;
_remainingTime = _shortBreakDuration;
}
} else {
_currentState = TimerState.work;
_remainingTime = _workDuration;
}
_startTimer();
});
}
void _addTask() {
if (_newTask.isNotEmpty) {
setState(() {
_tasks.add(_newTask);
_newTask = '';
});
}
}
void _removeTask(int index) {
setState(() {
_tasks.removeAt(index);
});
}
String _formatTime(int seconds) {
int minutes = seconds ~/ 60;
int remainingSeconds = seconds % 60;
return '${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}';
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('番茄时间管理'),
backgroundColor: Colors.red,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// 计时器部分
Container(
margin: const EdgeInsets.symmetric(vertical: 20),
child: Column(
children: [
Text(
_currentState == TimerState.work ? '工作时间' :
_currentState == TimerState.shortBreak ? '短休息' :
_currentState == TimerState.longBreak ? '长休息' :
'准备开始',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
Container(
width: 250,
height: 250,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _currentState == TimerState.work ? Colors.red.shade100 :
_currentState == TimerState.shortBreak || _currentState == TimerState.longBreak ? Colors.green.shade100 :
Colors.grey.shade100,
border: Border.all(
color: _currentState == TimerState.work ? Colors.red :
_currentState == TimerState.shortBreak || _currentState == TimerState.longBreak ? Colors.green :
Colors.grey,
width: 4,
),
),
child: Center(
child: Text(
_formatTime(_remainingTime),
style: TextStyle(
fontSize: 48,
fontWeight: FontWeight.bold,
color: _currentState == TimerState.work ? Colors.red :
_currentState == TimerState.shortBreak || _currentState == TimerState.longBreak ? Colors.green :
Colors.grey,
),
),
),
),
const SizedBox(height: 30),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_currentState == TimerState.stopped || _currentState == TimerState.paused)
ElevatedButton(
onPressed: _startTimer,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12),
),
child: const Text('开始'),
),
if (_currentState != TimerState.stopped && _currentState != TimerState.paused)
ElevatedButton(
onPressed: _pauseTimer,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12),
),
child: const Text('暂停'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: _stopTimer,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12),
),
child: const Text('停止'),
),
],
),
],
),
),
// 统计部分
Container(
margin: const EdgeInsets.symmetric(vertical: 20),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'统计',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
children: [
Text(
'$_completedPomodoros',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
const Text('完成的番茄'),
],
),
Column(
children: [
Text(
'${(_completedPomodoros * 25).toString()}',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
const Text('工作分钟'),
],
),
Column(
children: [
Text(
'${((_completedPomodoros ~/ 4) * 15 + ((_completedPomodoros % 4) * 5)).toString()}',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
const Text('休息分钟'),
],
),
],
),
],
),
),
// 任务管理部分
Container(
margin: const EdgeInsets.symmetric(vertical: 20),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'任务管理',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: TextField(
decoration: const InputDecoration(
hintText: '添加任务',
border: OutlineInputBorder(),
),
onChanged: (value) {
setState(() {
_newTask = value;
});
},
onSubmitted: (value) {
_addTask();
},
),
),
const SizedBox(width: 12),
ElevatedButton(
onPressed: _addTask,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('添加'),
),
],
),
const SizedBox(height: 12),
if (_tasks.isEmpty)
const Text('暂无任务'),
if (_tasks.isNotEmpty)
ListView.builder(
shrinkWrap: true,
itemCount: _tasks.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_tasks[index]),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
_removeTask(index);
},
),
);
},
),
],
),
),
// 设置部分
Container(
margin: const EdgeInsets.symmetric(vertical: 20),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'设置',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('工作时间 (分钟)'),
Row(
children: [
IconButton(
icon: const Icon(Icons.remove),
onPressed: () {
setState(() {
if (_workDuration > 5 * 60) {
_workDuration -= 5 * 60;
if (_currentState == TimerState.stopped) {
_remainingTime = _workDuration;
}
}
});
},
),
Text('${_workDuration ~/ 60}'),
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
setState(() {
if (_workDuration < 60 * 60) {
_workDuration += 5 * 60;
if (_currentState == TimerState.stopped) {
_remainingTime = _workDuration;
}
}
});
},
),
],
),
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('短休息时间 (分钟)'),
Row(
children: [
IconButton(
icon: const Icon(Icons.remove),
onPressed: () {
setState(() {
if (_shortBreakDuration > 1 * 60) {
_shortBreakDuration -= 1 * 60;
}
});
},
),
Text('${_shortBreakDuration ~/ 60}'),
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
setState(() {
if (_shortBreakDuration < 10 * 60) {
_shortBreakDuration += 1 * 60;
}
});
},
),
],
),
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('长休息时间 (分钟)'),
Row(
children: [
IconButton(
icon: const Icon(Icons.remove),
onPressed: () {
setState(() {
if (_longBreakDuration > 5 * 60) {
_longBreakDuration -= 5 * 60;
}
});
},
),
Text('${_longBreakDuration ~/ 60}'),
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
setState(() {
if (_longBreakDuration < 30 * 60) {
_longBreakDuration += 5 * 60;
}
});
},
),
],
),
],
),
],
),
),
],
),
),
);
}
}
10.2 依赖项
- flutter:Flutter 框架
- dart:async:提供 Timer 类,用于实现倒计时功能
- flutter/services.dart:提供 SystemChrome 类,用于设置系统 UI 样式
10.3 运行环境
- Flutter SDK:3.0.0 或更高版本
- Dart SDK:2.17.0 或更高版本
- 支持的平台:Android、iOS、Web、Windows、macOS、Linux