Flutter for OpenHarmony 视力保护提醒App实战 - 20-20-20计时器详解
摘要 本文详细介绍了20-20-20计时器的实现方法,该计时器基于护眼规则:每工作20分钟,看20英尺外物体20秒。文章从规则原理、计时器设计到具体代码实现进行了全面讲解。核心功能包括: 使用Flutter框架构建圆形计时器UI 通过Timer.periodic实现精确倒计时 状态管理控制计时器运行/暂停 时间到达提醒功能 美观的时间显示格式(两位数字) 实现采用状态变量管理时间、运行状态,并通过
20-20-20计时器是应用的核心功能,它实现了著名的20-20-20护眼规则。本文将详细讲解如何实现一个完整的20-20-20计时器,包括计时器的实现原理、时间管理和规则说明等内容。
20-20-20规则的介绍
20-20-20规则是一个简单但有效的护眼方法:每工作20分钟,看20英尺(约6米)外的东西,持续20秒。这个规则可以有效地缓解眼睛疲劳。这个规则的科学原理在于,当我们长时间盯着屏幕时,眼睛的睫状肌会持续收缩,导致眼睛疲劳。通过定期看远处的物体,可以让睫状肌放松,从而缓解眼睛疲劳。20分钟的工作时间是根据眼睛疲劳的规律制定的,经过科学研究证明是最有效的。
计时器页面的设计
计时器页面包含计时器显示、控制按钮和规则说明等部分。这样的设计可以让用户在一个页面内完成所有的计时器操作,提高了用户体验。
导入和类定义
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class BreakTimerDetail extends StatefulWidget {
const BreakTimerDetail({super.key});
State<BreakTimerDetail> createState() => _BreakTimerDetailState();
}
代码说明:这段代码导入了Flutter的Material设计库和屏幕适配库flutter_screenutil。定义了
BreakTimerDetail有状态组件,用于实现20-20-20计时器功能。使用StatefulWidget是因为计时器需要管理动态的时间状态。createState方法返回_BreakTimerDetailState实例,负责管理计时器的具体逻辑。这种设计模式使计时器能够响应用户交互并更新显示。
有状态Widget是Flutter中用于管理可变状态的基础组件。通过继承StatefulWidget,我们可以创建能够响应用户交互的动态UI。
状态变量初始化
class _BreakTimerDetailState extends State<BreakTimerDetail> {
int _minutes = 20;
int _seconds = 0;
bool _isRunning = false;
late Timer _timer;
代码说明:定义了四个状态变量来管理计时器的状态。
_minutes初始化为20,表示20分钟的工作时间。_seconds初始化为0,表示秒数。_isRunning初始化为false,表示计时器未运行。_timer是一个Timer对象,用于实现真正的计时功能。这些变量通过setState方法更新时,会触发Widget的重建,从而更新UI显示。这种状态管理方式是Flutter中最基础的状态管理模式。
状态变量的初始化是计时器实现的基础。通过合理的初始值设置,我们可以确保计时器在启动时显示正确的时间。
void initState() {
super.initState();
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_isRunning) {
setState(() {
if (_seconds > 0) {
_seconds--;
} else if (_minutes > 0) {
_minutes--;
_seconds = 59;
} else {
_isRunning = false;
_showCompletionDialog();
}
});
}
});
}
void _showCompletionDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('休息时间到'),
content: const Text('请看20英尺外的东西,持续20秒'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('确定'),
),
],
),
);
}
代码说明:initState方法在Widget初始化时调用,用于设置定时器。
Timer.periodic创建一个每秒执行一次的定时器。当_isRunning为true时,计时器会递减秒数。当秒数为0时,递减分钟数并将秒数重置为59。当分钟数和秒数都为0时,停止计时器并显示完成对话框。_showCompletionDialog方法显示一个提醒对话框,告知用户休息时间到了。这种设计确保了计时器的准确性和用户体验。
定时器的实现是计时器功能的核心。通过使用Timer.periodic,我们可以实现精确的倒计时功能。
主构建方法
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('20-20-20计时器'),
backgroundColor: const Color(0xFF2196F3),
foregroundColor: Colors.white,
),
body: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 40.h),
_buildTimerDisplay(),
SizedBox(height: 40.h),
_buildTimerControls(),
SizedBox(height: 40.h),
_buildRuleExplanation(),
SizedBox(height: 20.h),
],
),
),
);
}
代码说明:
build方法构建了整个计时器页面的布局。Scaffold提供了Material Design的基本框架。AppBar显示页面标题"20-20-20计时器",使用蓝色背景和白色文字。SingleChildScrollView包装了主体内容,确保当内容超过屏幕高度时可以滚动。通过SizedBox添加垂直间距,使页面布局更加清晰。这种分层的UI结构使代码易于理解和维护。
页面布局采用了分层设计,将不同的功能区域分开显示,提高了代码的可读性和可维护性。
计时器显示的实现
计时器显示是用户与应用交互的主要视觉焦点。一个好的计时器显示设计应该清晰、易读、美观。我们使用圆形设计来展示计时器,这样可以给用户一种时间循环的感觉,符合计时器的概念。
圆形计时器容器
Widget _buildTimerDisplay() {
return Center(
child: Container(
width: 200.w,
height: 200.w,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0xFF2196F3).withOpacity(0.1),
border: Border.all(
color: const Color(0xFF2196F3),
width: 4,
),
),
代码说明:
_buildTimerDisplay方法构建计时器显示区域。使用Center将内容居中显示。Container创建一个200x200的正方形容器。BoxDecoration定义了容器的样式,shape: BoxShape.circle使容器显示为圆形。蓝色的边框和半透明的背景使计时器看起来醒目而不刺眼。这种设计提供了良好的视觉焦点,用户可以快速定位计时器。
圆形设计在视觉上更加吸引人,也更符合计时器的概念。蓝色的配色与应用的整体风格保持一致。
计时器时间显示
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${_minutes.toString().padLeft(2, '0')}:${_seconds.toString().padLeft(2, '0')}',
style: TextStyle(
fontSize: 48.sp,
fontWeight: FontWeight.bold,
color: const Color(0xFF2196F3),
),
),
SizedBox(height: 8.h),
Text(
_isRunning ? '进行中' : '已暂停',
style: TextStyle(
fontSize: 14.sp,
color: Colors.grey,
),
),
],
),
),
),
);
}
代码说明:圆形容器内部使用
Column竖直排列时间显示和状态文本。padLeft(2, '0')方法确保分钟和秒数总是显示为两位数字,例如"09:05"。时间使用48sp的大字体和加粗显示,提高了可读性。状态文本显示计时器是否正在运行,使用较小的灰色字体。这种设计使用户能够清晰地看到当前的计时器状态。
时间显示的格式化处理确保了显示的一致性和专业性。大字体的使用使用户可以从远处清晰地看到计时器的时间。
计时器控制的实现
计时器控制部分提供了用户与计时器交互的方式。通过开始/暂停按钮和重置按钮,用户可以灵活地控制计时器的运行。这种设计提高了应用的易用性。
控制按钮布局
Widget _buildTimerControls() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton.icon(
onPressed: () {
setState(() => _isRunning = !_isRunning);
},
icon: Icon(_isRunning ? Icons.pause : Icons.play_arrow),
label: Text(_isRunning ? '暂停' : '开始'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2196F3),
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 12.h),
),
),
代码说明:
_buildTimerControls方法构建计时器控制区域。使用Padding添加水平边距。Row横向排列两个按钮。第一个ElevatedButton.icon是开始/暂停按钮,通过onPressed回调切换_isRunning状态。按钮的图标和标签根据_isRunning状态动态显示,当运行时显示暂停图标和"暂停"标签,否则显示播放图标和"开始"标签。这种设计提供了清晰的视觉反馈,用户可以快速理解按钮的功能。
开始/暂停按钮的设计采用了动态图标和标签,这样可以让用户清晰地了解按钮的当前功能。
重置按钮
SizedBox(width: 12.w),
ElevatedButton.icon(
onPressed: () {
setState(() {
_minutes = 20;
_seconds = 0;
_isRunning = false;
});
},
icon: const Icon(Icons.refresh),
label: const Text('重置'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 12.h),
),
),
],
),
代码说明:第二个
ElevatedButton.icon是重置按钮,使用灰色背景与开始/暂停按钮区分。onPressed回调将计时器重置为初始状态:20分钟、0秒、未运行。使用刷新图标表示重置功能。SizedBox在两个按钮之间添加水平间距。这种设计使用户能够快速重置计时器,方便重新开始计时。
重置按钮的灰色背景与蓝色的开始/暂停按钮形成对比,使用户能够快速区分两个按钮的功能。
规则提示文本
SizedBox(height: 16.h),
Text(
'20-20-20规则:每20分钟,看20英尺外的东西,持续20秒',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey,
fontStyle: FontStyle.italic,
),
),
],
),
);
}
代码说明:在按钮下方添加了规则提示文本,使用较小的灰色斜体字体。
textAlign: TextAlign.center使文本居中显示。这段文本简洁地总结了20-20-20规则,提醒用户规则的核心内容。这种设计提高了应用的易用性,用户无需查看其他文档就能理解规则。
提示文本的使用使应用更加自解释,用户可以直观地理解计时器的用途。
规则说明的实现
规则说明部分提供了详细的20-20-20规则解释。通过清晰的文字和视觉设计,用户可以深入理解这个护眼规则的含义和重要性。
规则说明容器
Widget _buildRuleExplanation() {
return Container(
margin: EdgeInsets.symmetric(horizontal: 16.w),
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'规则说明',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
),
SizedBox(height: 12.h),
_buildRuleItem('20分钟', '连续工作20分钟后'),
_buildRuleItem('20英尺', '看距离20英尺(约6米)外的东西'),
_buildRuleItem('20秒', '持续看20秒以放松眼睛'),
],
),
);
}
代码说明:
_buildRuleExplanation方法构建规则说明区域。使用Container创建一个白色卡片,添加圆角和阴影效果。Column竖直排列标题和三个规则项。标题使用较大的加粗字体。通过调用_buildRuleItem方法,为每个规则项创建对应的UI组件。这种设计使规则说明清晰易读,用户可以快速理解20-20-20规则的含义。
卡片设计提供了良好的视觉隔离,使规则说明更加突出。
规则项目组件
Widget _buildRuleItem(String title, String description) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 8.h),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 24.w,
height: 24.w,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF2196F3),
),
child: Center(
child: Text(
'✓',
style: TextStyle(
fontSize: 14.sp,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.bold,
),
),
Text(
description,
style: TextStyle(
fontSize: 12.sp,
color: Colors.grey,
),
),
],
),
),
],
),
);
}
代码说明:
_buildRuleItem是一个辅助方法,用于构建单个规则项。使用Row横向排列蓝色勾号、标题和描述。蓝色圆形容器中显示白色的勾号符号,表示这是规则的一部分。Expanded使标题和描述占据剩余空间。标题使用加粗字体,描述使用较小的灰色字体。这种设计提供了清晰的视觉层次,用户可以快速理解每个规则项的含义。
勾号符号的使用增强了视觉效果,使规则项更加突出。
计时器的工作原理详解
计时器使用StatefulWidget管理状态,通过setState方法更新UI。当用户点击开始按钮时,_isRunning变为true。在实际应用中,我们使用Timer类来实现真正的计时功能,每秒更新一次时间显示。
计时器的核心逻辑是在initState方法中创建的Timer.periodic对象。这个定时器每秒执行一次回调函数,检查_isRunning状态。如果计时器正在运行,就递减秒数。当秒数为0时,递减分钟数并将秒数重置为59。当分钟数和秒数都为0时,停止计时器并显示完成对话框。
这种设计确保了计时器的准确性。通过使用Timer.periodic,我们可以实现精确的倒计时功能,而不需要依赖其他复杂的计时机制。
屏幕适配处理
在整个页面中,我们使用flutter_screenutil库来处理屏幕适配。.w表示宽度单位,.h表示高度单位,.sp表示字体大小单位。这样可以确保在不同屏幕尺寸的设备上,UI元素的大小和间距都能正确显示。例如,EdgeInsets.symmetric(horizontal: 16.w)表示左右各有16个宽度单位的边距。
屏幕适配是现代移动应用开发的重要考虑因素。通过使用flutter_screenutil库,我们可以轻松地适配不同屏幕尺寸的设备,确保应用在各种设备上都能正常显示。
清理资源
void dispose() {
_timer.cancel();
super.dispose();
}
}
代码说明:dispose方法在Widget销毁时调用,用于清理资源。
_timer.cancel()停止定时器,防止内存泄漏。这是一个重要的最佳实践,确保应用不会因为未清理的定时器而浪费系统资源。
资源清理是应用开发的重要环节。通过在dispose方法中清理定时器,我们可以确保应用的性能和稳定性。
总结
通过以上的设计,我们实现了一个完整的20-20-20计时器。用户可以使用计时器来遵循20-20-20规则,保护眼睛健康。这样的设计使得用户可以方便地进行眼睛保护。
在实际应用中,我们可以进一步扩展这个系统,添加自动计时、提醒通知等功能。例如,可以使用Timer.periodic实现自动倒计时,使用本地通知提醒用户休息。还可以添加统计功能,记录用户使用计时器的次数和时间,帮助用户了解自己的眼睛保护情况。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)