在这里插入图片描述

快速护眼锻炼是应用的重要功能,它提供了多种眼睛锻炼方法来帮助用户缓解眼睛疲劳。本文将详细讲解如何实现一个快速护眼锻炼页面,包括锻炼方法的选择、详细说明和开始锻炼等功能。

快速护眼锻炼页面的设计

快速护眼锻炼页面包含锻炼方法选择器、锻炼详情展示和开始锻炼按钮等多个部分。这样的设计可以让用户方便地选择和进行眼睛锻炼。

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

class QuickExerciseDetail extends StatefulWidget {
  const QuickExerciseDetail({super.key});

  
  State<QuickExerciseDetail> createState() => _QuickExerciseDetailState();
}

class _QuickExerciseDetailState extends State<QuickExerciseDetail> {
  int _selectedExercise = 0;

  final exercises = [
    {
      'title': '眼球转动',
      'description': '缓慢转动眼球,顺时针和逆时针各10次',
      'duration': '2分钟',
      'icon': Icons.rotate_right,
    },
    {
      'title': '远近调焦',
      'description': '看远处30秒,再看近处30秒,重复5次',
      'duration': '3分钟',
      'icon': Icons.visibility,
    },
    {
      'title': '眼睛放松',
      'description': '闭眼休息,深呼吸,放松眼部肌肉',
      'duration': '5分钟',
      'icon': Icons.spa,
    },
    {
      'title': '眼部按摩',
      'description': '轻轻按摩眼周穴位,促进血液循环',
      'duration': '3分钟',
      'icon': Icons.touch_app,
    },
  ];

这段代码定义了QuickExerciseDetail有状态组件和四种不同的眼睛锻炼方法。每种锻炼方法都包含标题、描述、耗时和对应的图标,这样的数据结构便于后续的UI展示和用户交互。通过使用StatefulWidget,我们可以管理用户选择的锻炼方法状态,实现动态的UI更新。这种设计模式遵循了Flutter的最佳实践,将数据和UI逻辑分离,提高了代码的可维护性和可读性。

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('快速锻炼'),
        backgroundColor: const Color(0xFFE91E63),
        foregroundColor: Colors.white,
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            _buildExerciseSelector(),
            SizedBox(height: 16.h),
            _buildExerciseDetail(),
            SizedBox(height: 16.h),
            _buildStartButton(),
            SizedBox(height: 20.h),
          ],
        ),
      ),
    );
  }

build方法构建了整个页面的布局结构。使用Scaffold作为基础框架,提供了标准的Material Design布局。AppBar显示页面标题和粉红色的主题色,SingleChildScrollView包装了主体内容,确保当内容超过屏幕高度时可以滚动。通过将页面分解为三个主要部分(选择器、详情、按钮),代码结构清晰,易于维护和扩展。

快速护眼锻炼页面使用StatefulWidget来管理选中的锻炼方法。我们定义了四种不同的锻炼方法,每种方法都有自己的标题、描述、耗时和图标。

锻炼方法选择器的实现

锻炼方法选择器允许用户在不同的锻炼方法之间切换。

  Widget _buildExerciseSelector() {
    return SizedBox(
      height: 100.h,
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        padding: EdgeInsets.symmetric(horizontal: 16.w),
        itemCount: exercises.length,
        itemBuilder: (context, index) {
          final isSelected = _selectedExercise == index;
          return GestureDetector(
            onTap: () => setState(() => _selectedExercise = index),
            child: Container(
              width: 80.w,
              margin: EdgeInsets.only(right: 12.w),
              decoration: BoxDecoration(
                color: isSelected ? const Color(0xFFE91E63) : Colors.white,
                borderRadius: BorderRadius.circular(12.r),
                border: Border.all(
                  color: isSelected ? const Color(0xFFE91E63) : Colors.grey[300]!,
                  width: 2,
                ),
              ),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(
                    exercises[index]['icon'] as IconData,
                    color: isSelected ? Colors.white : const Color(0xFFE91E63),
                    size: 24.sp,
                  ),
                  SizedBox(height: 4.h),
                  Text(
                    exercises[index]['title'] as String,
                    textAlign: TextAlign.center,
                    style: TextStyle(
                      fontSize: 10.sp,
                      color: isSelected ? Colors.white : Colors.black,
                      fontWeight: FontWeight.w500,
                    ),
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }

代码说明: _buildExerciseSelector()方法使用ListView.builder创建水平滚动的锻炼方法选择器。通过GestureDetector捕获用户的点击事件,调用setState()更新选中的锻炼索引。每个卡片根据isSelected状态动态改变背景色、图标颜色和文字颜色,选中时为粉红色,未选中时为白色。SizedBox设置固定高度为100.h,确保选择器的尺寸一致。ListView.builderscrollDirection: Axis.horizontal实现了水平滚动效果。

选择器的交互设计: 这个选择器采用了状态驱动的UI设计模式。通过_selectedExercise状态变量管理选中项,确保UI与数据状态同步。使用ListView.builder实现高效的列表渲染,只构建可见的卡片。水平滚动的设计使得用户可以轻松浏览所有锻炼方法。颜色变化提供了清晰的视觉反馈,帮助用户快速识别当前选中的锻炼。这种设计提供了流畅的用户交互体验,同时保持了代码的可维护性。

锻炼详情的展示

锻炼详情部分显示选中锻炼方法的详细信息。

  Widget _buildExerciseDetail() {
    final exercise = exercises[_selectedExercise];
    return Container(
      margin: EdgeInsets.symmetric(horizontal: 16.w),
      padding: EdgeInsets.all(20.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: [
          Row(
            children: [
              Icon(
                exercise['icon'] as IconData,
                color: const Color(0xFFE91E63),
                size: 32.sp,
              ),
              SizedBox(width: 12.w),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      exercise['title'] as String,
                      style: TextStyle(
                        fontSize: 18.sp,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    Text(
                      '耗时: ${exercise['duration']}',
                      style: TextStyle(
                        fontSize: 12.sp,
                        color: Colors.grey,
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
          SizedBox(height: 16.h),
          Text(
            '说明',
            style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.bold),
          ),
          SizedBox(height: 8.h),
          Text(
            exercise['description'] as String,
            style: TextStyle(fontSize: 14.sp, color: Colors.grey[700]),
          ),
        ],
      ),
    );
  }

代码说明: _buildExerciseDetail()方法构建锻炼详情卡片,显示选中锻炼的完整信息。使用RowExpanded布局图标、标题和耗时,确保响应式设计。下方显示详细的锻炼说明文本。通过BoxShadow添加阴影效果,提升视觉层次感。Container使用EdgeInsets.symmetric设置对称的外边距和内边距,确保卡片与屏幕边缘的距离一致。

详情卡片的设计优势: 这个详情卡片采用了信息分层展示的设计模式。上部分为快速信息(图标、标题、耗时),下部分为详细说明,符合用户的阅读习惯。使用ContainerBoxDecoration创建统一的卡片风格,与整个应用的设计语言保持一致。粉红色的图标与应用主题色相呼应,提高了品牌识别度。这种设计提高了信息的可读性和用户体验。

开始锻炼按钮的实现

开始锻炼按钮允许用户开始进行选中的锻炼。

  Widget _buildStartButton() {
    return Padding(
      padding: EdgeInsets.symmetric(horizontal: 16.w),
      child: SizedBox(
        width: double.infinity,
        child: ElevatedButton.icon(
          onPressed: () {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text('开始${exercises[_selectedExercise]['title']}'),
                backgroundColor: const Color(0xFFE91E63),
              ),
            );
          },
          icon: const Icon(Icons.play_arrow),
          label: const Text('开始锻炼'),
          style: ElevatedButton.styleFrom(
            backgroundColor: const Color(0xFFE91E63),
            foregroundColor: Colors.white,
            padding: EdgeInsets.symmetric(vertical: 12.h),
          ),
        ),
      ),
    );
  }
}

代码说明: _buildStartButton()方法创建一个全宽的粉红色按钮,使用ElevatedButton.icon显示播放图标和"开始锻炼"文本。点击时通过ScaffoldMessenger.showSnackBar()显示提示信息,告知用户已开始的锻炼名称。按钮使用SizedBoxPadding实现响应式布局。ElevatedButton.styleFrom设置按钮的背景色、前景色和内边距。double.infinity使按钮宽度填满父容器。

按钮交互设计: 这个按钮采用了反馈驱动的交互设计。通过SnackBar提供即时的用户反馈,确认用户的操作已被系统接收。按钮的粉红色主题色与整个应用保持一致,提高了品牌识别度。全宽设计使得按钮易于点击,提升了移动应用的可用性。播放图标的使用直观地表达了"开始"的含义,降低了用户的理解成本。

锻炼计时器

锻炼计时器是快速护眼锻炼的重要组件,用于帮助用户按时完成锻炼。

class ExerciseTimer {
  int _remainingSeconds = 0;
  bool _isRunning = false;
  late Timer _timer;
  final Function(int) onTick;
  final Function() onComplete;

  ExerciseTimer({
    required this.onTick,
    required this.onComplete,
  });

  void startTimer(int durationInSeconds) {
    _remainingSeconds = durationInSeconds;
    _isRunning = true;
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      _remainingSeconds--;
      onTick(_remainingSeconds);
      if (_remainingSeconds <= 0) {
        stopTimer();
        onComplete();
      }
    });
  }

  void pauseTimer() {
    if (_isRunning) {
      _timer.cancel();
      _isRunning = false;
    }
  }

  void resumeTimer() {
    if (!_isRunning && _remainingSeconds > 0) {
      _isRunning = true;
      _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
        _remainingSeconds--;
        onTick(_remainingSeconds);
        if (_remainingSeconds <= 0) {
          stopTimer();
          onComplete();
        }
      });
    }
  }

  void stopTimer() {
    if (_isRunning) {
      _timer.cancel();
      _isRunning = false;
      _remainingSeconds = 0;
    }
  }

  int getRemainingSeconds() => _remainingSeconds;
  bool isRunning() => _isRunning;
}

代码说明: 这部分代码定义了ExerciseTimer类,用于管理锻炼的计时功能。_remainingSeconds存储剩余的秒数,_isRunning标记计时器是否正在运行。onTickonComplete是回调函数,分别在每秒更新和计时完成时调用。startTimer()方法启动计时器,使用Timer.periodic()每秒触发一次回调。pauseTimer()方法暂停计时器,resumeTimer()方法恢复计时。stopTimer()方法停止计时器并重置状态。getRemainingSeconds()isRunning()是getter方法,用于获取计时器的当前状态。

计时器的核心设计: 这个计时器采用了回调模式,通过onTickonComplete回调函数与UI层解耦。Timer.periodic()提供了精确的定时功能,每秒触发一次。暂停和恢复功能通过取消和重新创建定时器实现,确保了计时的准确性。这种设计使得计时器可以独立于UI存在,便于单元测试和代码复用。

锻炼记录管理

锻炼记录管理用于跟踪用户的锻炼历史和统计数据。

class ExerciseRecord {
  final String id;
  final String exerciseTitle;
  final DateTime timestamp;
  final int durationInSeconds;
  final bool completed;
  final String notes;

  ExerciseRecord({
    required this.id,
    required this.exerciseTitle,
    required this.timestamp,
    required this.durationInSeconds,
    required this.completed,
    required this.notes,
  });

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'exerciseTitle': exerciseTitle,
      'timestamp': timestamp.toIso8601String(),
      'durationInSeconds': durationInSeconds,
      'completed': completed,
      'notes': notes,
    };
  }

  factory ExerciseRecord.fromJson(Map<String, dynamic> json) {
    return ExerciseRecord(
      id: json['id'],
      exerciseTitle: json['exerciseTitle'],
      timestamp: DateTime.parse(json['timestamp']),
      durationInSeconds: json['durationInSeconds'],
      completed: json['completed'],
      notes: json['notes'],
    );
  }
}

代码说明: 这部分代码定义了ExerciseRecord类,用于表示单条锻炼记录。包含id(唯一标识符)、exerciseTitle(锻炼名称)、timestamp(锻炼时间)、durationInSeconds(锻炼时长)、completed(是否完成)和notes(备注)。toJson()方法将对象转换为JSON格式,便于存储。fromJson()工厂构造函数从JSON数据重建对象。这种设计支持数据的序列化和反序列化,便于持久化存储。DateTime.parse()toIso8601String()确保了时间数据的正确处理。

数据模型设计: 这个模型包含了锻炼记录的所有关键信息,支持完整的生命周期管理。通过completed字段可以区分已完成和未完成的锻炼。notes字段允许用户添加个人备注,增加了应用的灵活性。toJson()fromJson()方法遵循了Dart的标准序列化模式,便于与数据库或网络API集成。

class ExerciseRecordManager {
  static final ExerciseRecordManager _instance = ExerciseRecordManager._internal();
  final List<ExerciseRecord> _records = [];

  factory ExerciseRecordManager() {
    return _instance;
  }

  ExerciseRecordManager._internal();

  void addRecord(ExerciseRecord record) {
    _records.add(record);
  }

  List<ExerciseRecord> getRecordsByDate(DateTime date) {
    return _records
        .where((record) =>
            record.timestamp.year == date.year &&
            record.timestamp.month == date.month &&
            record.timestamp.day == date.day)
        .toList();
  }

  int getCompletedCountByDate(DateTime date) {
    return getRecordsByDate(date).where((record) => record.completed).length;
  }

  int getTotalDurationByDate(DateTime date) {
    return getRecordsByDate(date)
        .fold<int>(0, (prev, record) => prev + record.durationInSeconds);
  }

  List<ExerciseRecord> getRecordsByExercise(String exerciseTitle) {
    return _records.where((record) => record.exerciseTitle == exerciseTitle).toList();
  }
}

代码说明: ExerciseRecordManager类采用单例模式管理所有的锻炼记录。_records列表存储所有的锻炼记录。addRecord()方法添加新的锻炼记录。getRecordsByDate()方法根据指定日期获取该天的所有锻炼记录,通过比较年月日来精确匹配。getCompletedCountByDate()方法计算指定日期完成的锻炼次数。getTotalDurationByDate()方法计算指定日期的总锻炼时长,使用fold()函数进行求和。getRecordsByExercise()方法根据锻炼名称获取相关的所有记录。

数据管理设计: 单例模式确保了全局数据的一致性和访问的便利性。使用where()fold()等函数式编程方法进行数据过滤和聚合,代码简洁高效。按日期和锻炼类型的查询方法支持多维度的数据分析,使得应用可以轻松生成各种统计报告。这种设计提供了灵活的数据查询接口和良好的可扩展性。

锻炼效果评估

锻炼效果评估用于分析用户的锻炼成果和改进建议。

class ExerciseEffectAnalyzer {
  static Map<String, dynamic> analyzeWeeklyProgress(
    List<ExerciseRecord> records,
  ) {
    if (records.isEmpty) {
      return {
        'totalSessions': 0,
        'completionRate': 0.0,
        'totalDuration': 0,
        'averageDuration': 0,
        'trend': '无数据',
      };
    }

    final completedCount = records.where((r) => r.completed).length;
    final completionRate = (completedCount / records.length) * 100;
    final totalDuration = records.fold<int>(0, (prev, r) => prev + r.durationInSeconds);
    final averageDuration = totalDuration ~/ records.length;

    return {
      'totalSessions': records.length,
      'completionRate': completionRate,
      'totalDuration': totalDuration,
      'averageDuration': averageDuration,
      'trend': completionRate >= 80 ? '优秀' : completionRate >= 60 ? '良好' : '需要改善',
    };
  }

  static String getRecommendation(Map<String, dynamic> analysis) {
    final completionRate = analysis['completionRate'] as double;
    if (completionRate >= 80) {
      return '坚持保持,你的锻炼效果很好!';
    } else if (completionRate >= 60) {
      return '继续努力,增加锻炼频率会更有效果。';
    } else {
      return '建议增加锻炼次数,每天至少进行一次护眼锻炼。';
    }
  }

  static Color getTrendColor(String trend) {
    switch (trend) {
      case '优秀':
        return const Color(0xFF4CAF50);
      case '良好':
        return const Color(0xFF2196F3);
      default:
        return const Color(0xFFFFC107);
    }
  }
}

代码说明: ExerciseEffectAnalyzer类提供了静态方法用于分析锻炼效果。analyzeWeeklyProgress()方法计算总锻炼次数、完成率、总时长和平均时长。完成率通过已完成的记录数除以总记录数计算。根据完成率判断锻炼趋势(优秀/良好/需要改善)。getRecommendation()方法根据分析结果提供个性化的建议。getTrendColor()方法根据趋势返回对应的颜色。空列表时返回默认的零值数据。

分析设计模式: 这个分析器采用了多维度评估方法,通过完成率、总时长和平均时长等指标全面评估锻炼效果。三级评分系统提供了清晰的反馈。个性化建议根据用户的实际情况提供针对性的指导。颜色映射使得用户可以直观地理解评估结果。静态方法设计使得分析功能可以独立使用,便于测试和复用。

总结

通过以上的设计,我们实现了一个完整的快速护眼锻炼系统。包括锻炼计时器、锻炼记录管理和锻炼效果评估。用户可以进行定时的眼睛锻炼、查看锻炼历史、获得效果评估和改进建议。这样的设计使得用户能够系统地进行护眼锻炼,并了解锻炼的效果。

在实际应用中,我们可以进一步扩展这个系统,添加更多的锻炼方法、社交分享功能、排行榜等功能。

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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐