在这里插入图片描述

前言

训练计划是帮助用户系统性提升运动能力的核心功能。通过科学的训练安排,用户可以循序渐进地达成运动目标,避免过度训练或训练不足。本文将详细介绍如何在Flutter与OpenHarmony平台上实现专业的训练计划组件,包括计划创建、课程安排、进度追踪、智能调整等功能模块的完整实现方案。

训练计划的设计需要考虑运动科学原理,包括渐进超负荷、恢复周期、训练多样性等。我们需要为不同水平的用户提供适合的训练方案,并根据用户的完成情况动态调整计划难度。

Flutter训练计划模型

class TrainingPlan {
  final String id;
  final String name;
  final String description;
  final String level;
  final int durationWeeks;
  final String goal;
  final List<TrainingWeek> weeks;
  final DateTime startDate;
  final int completedSessions;
  
  TrainingPlan({
    required this.id,
    required this.name,
    required this.description,
    required this.level,
    required this.durationWeeks,
    required this.goal,
    required this.weeks,
    required this.startDate,
    this.completedSessions = 0,
  });
  
  int get totalSessions => weeks.fold(0, (sum, w) => sum + w.sessions.length);
  double get progressPercentage => totalSessions > 0 ? completedSessions / totalSessions * 100 : 0;
  int get currentWeek => DateTime.now().difference(startDate).inDays ~/ 7 + 1;
  
  TrainingSession? get todaySession {
    int dayOfWeek = DateTime.now().weekday;
    if (currentWeek <= weeks.length) {
      return weeks[currentWeek - 1].sessions.firstWhere(
        (s) => s.dayOfWeek == dayOfWeek,
        orElse: () => TrainingSession.rest(),
      );
    }
    return null;
  }
}

class TrainingWeek {
  final int weekNumber;
  final String focus;
  final List<TrainingSession> sessions;
  
  TrainingWeek({required this.weekNumber, required this.focus, required this.sessions});
}

训练计划模型定义了完整的训练方案结构。计划包含名称、描述、难度级别、持续周数和目标。weeks列表包含每周的训练安排。progressPercentage计算整体完成进度,currentWeek根据开始日期计算当前是第几周。todaySession属性返回今天应该进行的训练课程,如果今天是休息日则返回休息课程。这种层级结构清晰地组织了训练计划的所有内容。

Flutter训练课程模型

class TrainingSession {
  final String id;
  final String name;
  final String type;
  final int dayOfWeek;
  final Duration targetDuration;
  final double? targetDistance;
  final String? targetPace;
  final List<TrainingInterval> intervals;
  final bool isCompleted;
  final String description;
  
  TrainingSession({
    required this.id,
    required this.name,
    required this.type,
    required this.dayOfWeek,
    required this.targetDuration,
    this.targetDistance,
    this.targetPace,
    this.intervals = const [],
    this.isCompleted = false,
    this.description = '',
  });
  
  factory TrainingSession.rest() {
    return TrainingSession(
      id: 'rest',
      name: '休息日',
      type: 'rest',
      dayOfWeek: 0,
      targetDuration: Duration.zero,
      description: '今天是休息日,让身体充分恢复',
    );
  }
  
  String get typeIcon {
    switch (type) {
      case 'easy': return '🚶';
      case 'tempo': return '🏃';
      case 'interval': return '⚡';
      case 'long': return '🏃‍♂️';
      case 'rest': return '😴';
      default: return '🏃';
    }
  }
}

class TrainingInterval {
  final String name;
  final Duration duration;
  final String intensity;
  
  TrainingInterval({required this.name, required this.duration, required this.intensity});
}

训练课程模型定义了单次训练的详细内容。type区分不同类型的训练:轻松跑、节奏跑、间歇跑、长距离跑和休息。targetDuration、targetDistance和targetPace定义训练目标。intervals列表用于间歇训练,定义每个间歇段的时长和强度。rest工厂方法创建休息日课程。typeIcon属性为不同类型返回对应的emoji图标。这种模型支持从简单的持续跑到复杂的间歇训练各种课程类型。

OpenHarmony训练计划存储

import relationalStore from '@ohos.data.relationalStore';

class TrainingPlanStorage {
  private rdbStore: relationalStore.RdbStore | null = null;
  
  async initDatabase(context: Context): Promise<void> {
    const config: relationalStore.StoreConfig = {
      name: 'training.db',
      securityLevel: relationalStore.SecurityLevel.S1,
    };
    this.rdbStore = await relationalStore.getRdbStore(context, config);
    
    await this.rdbStore.executeSql(
      'CREATE TABLE IF NOT EXISTS plans (id TEXT PRIMARY KEY, name TEXT, data TEXT, start_date TEXT, completed_sessions INTEGER)'
    );
    
    await this.rdbStore.executeSql(
      'CREATE TABLE IF NOT EXISTS session_records (id TEXT PRIMARY KEY, plan_id TEXT, session_id TEXT, completed_date TEXT, actual_duration INTEGER, actual_distance REAL)'
    );
  }
  
  async savePlan(plan: object): Promise<void> {
    if (this.rdbStore) {
      let valueBucket = {
        'id': plan['id'],
        'name': plan['name'],
        'data': JSON.stringify(plan),
        'start_date': plan['startDate'],
        'completed_sessions': 0,
      };
      await this.rdbStore.insert('plans', valueBucket);
    }
  }
  
  async markSessionCompleted(planId: string, sessionId: string, duration: number, distance: number): Promise<void> {
    if (this.rdbStore) {
      let recordBucket = {
        'id': Date.now().toString(),
        'plan_id': planId,
        'session_id': sessionId,
        'completed_date': new Date().toISOString(),
        'actual_duration': duration,
        'actual_distance': distance,
      };
      await this.rdbStore.insert('session_records', recordBucket);
      
      let predicates = new relationalStore.RdbPredicates('plans');
      predicates.equalTo('id', planId);
      let resultSet = await this.rdbStore.query(predicates, ['completed_sessions']);
      if (resultSet.goToFirstRow()) {
        let completed = resultSet.getLong(resultSet.getColumnIndex('completed_sessions'));
        await this.rdbStore.update({ 'completed_sessions': completed + 1 }, predicates);
      }
      resultSet.close();
    }
  }
}

训练计划存储服务管理计划和完成记录。plans表存储计划基本信息和完整数据的JSON字符串。session_records表记录每次训练的完成情况,包括实际时长和距离。markSessionCompleted方法在用户完成训练后调用,插入完成记录并更新计划的完成课程数。这种设计支持计划进度追踪和训练数据分析。

Flutter训练计划卡片

class TrainingPlanCard extends StatelessWidget {
  final TrainingPlan plan;
  final VoidCallback onTap;
  
  const TrainingPlanCard({Key? key, required this.plan, required this.onTap}) : super(key: key);
  
  
  Widget build(BuildContext context) {
    return Card(
      margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(12),
        child: Padding(
          padding: EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(plan.name, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                  Container(
                    padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                    decoration: BoxDecoration(
                      color: _getLevelColor(plan.level),
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child: Text(plan.level, style: TextStyle(color: Colors.white, fontSize: 12)),
                  ),
                ],
              ),
              SizedBox(height: 8),
              Text(plan.description, style: TextStyle(color: Colors.grey), maxLines: 2),
              SizedBox(height: 12),
              Row(
                children: [
                  Icon(Icons.calendar_today, size: 16, color: Colors.grey),
                  SizedBox(width: 4),
                  Text('${plan.durationWeeks}周', style: TextStyle(color: Colors.grey)),
                  SizedBox(width: 16),
                  Icon(Icons.flag, size: 16, color: Colors.grey),
                  SizedBox(width: 4),
                  Text(plan.goal, style: TextStyle(color: Colors.grey)),
                ],
              ),
              SizedBox(height: 12),
              LinearProgressIndicator(
                value: plan.progressPercentage / 100,
                backgroundColor: Colors.grey[200],
              ),
              SizedBox(height: 4),
              Text('进度: ${plan.progressPercentage.toStringAsFixed(0)}% (${plan.completedSessions}/${plan.totalSessions}课)', style: TextStyle(fontSize: 12, color: Colors.grey)),
            ],
          ),
        ),
      ),
    );
  }
  
  Color _getLevelColor(String level) {
    switch (level) {
      case '初级': return Colors.green;
      case '中级': return Colors.orange;
      case '高级': return Colors.red;
      default: return Colors.blue;
    }
  }
}

训练计划卡片展示计划的概览信息。顶部显示计划名称和难度级别标签,中间显示描述、周数和目标,底部显示进度条和完成情况。难度级别使用不同颜色区分,初级绿色、中级橙色、高级红色。进度条直观展示计划完成进度。这种卡片设计让用户快速了解计划的基本信息和当前状态,点击可以查看详细的周计划和课程安排。

Flutter今日训练组件

class TodayTrainingCard extends StatelessWidget {
  final TrainingSession? session;
  final VoidCallback onStart;
  
  const TodayTrainingCard({Key? key, this.session, required this.onStart}) : super(key: key);
  
  
  Widget build(BuildContext context) {
    if (session == null) {
      return Card(
        margin: EdgeInsets.all(16),
        child: Padding(
          padding: EdgeInsets.all(24),
          child: Center(child: Text('暂无训练计划', style: TextStyle(color: Colors.grey))),
        ),
      );
    }
    
    bool isRest = session!.type == 'rest';
    
    return Card(
      margin: EdgeInsets.all(16),
      child: Container(
        decoration: BoxDecoration(
          gradient: isRest ? null : LinearGradient(
            colors: [Colors.blue, Colors.blueAccent],
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
          ),
          borderRadius: BorderRadius.circular(12),
        ),
        child: Padding(
          padding: EdgeInsets.all(20),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                children: [
                  Text(session!.typeIcon, style: TextStyle(fontSize: 32)),
                  SizedBox(width: 12),
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('今日训练', style: TextStyle(color: isRest ? Colors.grey : Colors.white70)),
                      Text(session!.name, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: isRest ? Colors.black : Colors.white)),
                    ],
                  ),
                ],
              ),
              SizedBox(height: 16),
              Text(session!.description, style: TextStyle(color: isRest ? Colors.grey : Colors.white70)),
              if (!isRest) ...[
                SizedBox(height: 16),
                Row(
                  children: [
                    if (session!.targetDuration.inMinutes > 0)
                      _buildTargetChip('${session!.targetDuration.inMinutes}分钟'),
                    if (session!.targetDistance != null)
                      _buildTargetChip('${session!.targetDistance}公里'),
                    if (session!.targetPace != null)
                      _buildTargetChip('配速${session!.targetPace}'),
                  ],
                ),
                SizedBox(height: 16),
                SizedBox(
                  width: double.infinity,
                  child: ElevatedButton(
                    onPressed: session!.isCompleted ? null : onStart,
                    style: ElevatedButton.styleFrom(backgroundColor: Colors.white, foregroundColor: Colors.blue),
                    child: Text(session!.isCompleted ? '已完成' : '开始训练'),
                  ),
                ),
              ],
            ],
          ),
        ),
      ),
    );
  }
  
  Widget _buildTargetChip(String text) {
    return Container(
      margin: EdgeInsets.only(right: 8),
      padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
      decoration: BoxDecoration(color: Colors.white24, borderRadius: BorderRadius.circular(16)),
      child: Text(text, style: TextStyle(color: Colors.white)),
    );
  }
}

今日训练组件突出显示当天应该进行的训练课程。使用渐变背景和大图标吸引用户注意。显示训练类型、名称、描述和目标参数。休息日使用普通样式,训练日使用蓝色渐变背景。底部的开始按钮引导用户开始训练,已完成的课程按钮变为禁用状态。这种设计让用户打开应用就能看到今天的训练任务,降低了开始训练的门槛。

OpenHarmony训练提醒服务

import reminderAgentManager from '@ohos.reminderAgentManager';

class TrainingReminderService {
  async setTrainingReminder(session: object, hour: number, minute: number): Promise<number> {
    let reminderRequest: reminderAgentManager.ReminderRequestAlarm = {
      reminderType: reminderAgentManager.ReminderType.REMINDER_TYPE_ALARM,
      hour: hour,
      minute: minute,
      daysOfWeek: [session['dayOfWeek']],
      title: '训练提醒',
      content: `今天的训练: ${session['name']}`,
      ringDuration: 10,
      snoozeTimes: 1,
      snoozeInterval: 10,
    };
    
    return await reminderAgentManager.publishReminder(reminderRequest);
  }
  
  async cancelReminder(reminderId: number): Promise<void> {
    await reminderAgentManager.cancelReminder(reminderId);
  }
}

训练提醒服务在训练日提醒用户进行训练。根据课程的dayOfWeek设置提醒的重复日期,确保只在训练日提醒。提醒内容包含当天的训练名称,帮助用户了解训练内容。这种提醒机制帮助用户坚持训练计划,不会因为忘记而错过训练。

Flutter周计划视图

class WeekPlanView extends StatelessWidget {
  final TrainingWeek week;
  final Function(TrainingSession) onSessionTap;
  
  const WeekPlanView({Key? key, required this.week, required this.onSessionTap}) : super(key: key);
  
  
  Widget build(BuildContext context) {
    List<String> dayNames = ['', '周一', '周二', '周三', '周四', '周五', '周六', '周日'];
    
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('第${week.weekNumber}周', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
              Text('重点: ${week.focus}', style: TextStyle(color: Colors.grey)),
            ],
          ),
        ),
        ...List.generate(7, (index) {
          int day = index + 1;
          var session = week.sessions.firstWhere(
            (s) => s.dayOfWeek == day,
            orElse: () => TrainingSession.rest()..dayOfWeek == day,
          );
          
          return ListTile(
            leading: Container(
              width: 40,
              height: 40,
              decoration: BoxDecoration(
                color: session.type == 'rest' ? Colors.grey[200] : Colors.blue.withOpacity(0.1),
                borderRadius: BorderRadius.circular(8),
              ),
              child: Center(child: Text(session.typeIcon)),
            ),
            title: Text(dayNames[day]),
            subtitle: Text(session.name),
            trailing: session.isCompleted ? Icon(Icons.check_circle, color: Colors.green) : null,
            onTap: () => onSessionTap(session),
          );
        }),
      ],
    );
  }
}

周计划视图展示一周的训练安排。顶部显示周数和本周训练重点,下方列出周一到周日的每日课程。每天显示训练类型图标、星期几和课程名称,已完成的课程显示绿色勾选。休息日使用灰色背景区分。这种视图让用户可以预览整周的训练安排,合理规划时间。点击某天可以查看该课程的详细内容。

总结

本文全面介绍了Flutter与OpenHarmony平台上训练计划组件的实现方案。从计划模型到课程安排,从进度追踪到训练提醒,涵盖了训练计划功能的各个方面。通过科学的训练安排和便捷的进度管理,我们可以帮助用户系统性地提升运动能力,实现运动目标。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐