鸿蒙+Flutter 跨平台开发——基于日历视图的生理周期计算逻辑

🚀运行效果展示

在这里插入图片描述

在这里插入图片描述

一、前言

1.1 背景

随着移动互联网的快速发展,跨平台开发技术逐渐成为移动应用开发的主流趋势。Flutter作为Google推出的跨平台UI框架,凭借其"一次编写,处处运行"的特性,受到了广大开发者的青睐。而华为的HarmonyOS作为全新的分布式操作系统,也在不断扩大其生态影响力。将Flutter与HarmonyOS结合,开发跨平台应用,具有重要的实际意义和市场价值。

1.2 生理周期记录应用的重要性

生理周期记录应用是女性健康管理的重要工具,它可以帮助女性:

  • 追踪和记录生理周期
  • 预测下次生理期、排卵期和易孕期
  • 分析周期规律和身体状况
  • 提高健康意识和生活质量

基于日历视图的生理周期计算应用,具有直观、易用、功能全面等特点,能够更好地满足女性用户的需求。

二、应用介绍

2.1 应用功能概述

本应用是一款基于Flutter和HarmonyOS开发的跨平台生理周期记录应用,主要功能包括:

  • 📅 日历视图:直观展示生理周期记录和预测
  • 📊 周期预测:基于历史记录计算平均周期和经期长度,预测下次生理期、排卵期和易孕期
  • 📝 记录管理:支持添加、编辑和删除生理周期记录
  • 📈 统计分析:展示周期趋势和身体状况分析
  • ⚙️ 个性化设置:支持自定义平均周期和经期长度

2.2 技术栈

技术 版本 用途
Flutter 3.16.0 跨平台UI框架
Dart 3.2.0 开发语言
Provider 6.1.1 状态管理
HarmonyOS 4.0 目标平台

2.3 应用架构

┌───────────────────────────────────────────────────────────┐
│                    应用层 (UI)                            │
├───────────────────────────────────────────────────────────┤
│  主页(HomeScreen)  ├─── 记录页(AddRecordScreen)           │
│                     │                                     │
│                     ├─── 统计页(StatisticsScreen)         │
│                     │                                     │
│                     └─── 设置页(SettingsScreen)           │
├───────────────────────────────────────────────────────────┤
│                    状态管理层 (Provider)                  │
├───────────────────────────────────────────────────────────┤
│              MenstrualProvider  ────  PasswordProvider    │
├───────────────────────────────────────────────────────────┤
│                    服务层 (Service)                       │
├───────────────────────────────────────────────────────────┤
│       MenstrualStorageService  ────  StorageService       │
├───────────────────────────────────────────────────────────┤
│                    数据层 (Model)                         │
├───────────────────────────────────────────────────────────┤
│        MenstrualRecord  ───────────  PasswordItem         │
└───────────────────────────────────────────────────────────┘

三、核心功能实现及代码展示

3.1 数据模型设计

3.1.1 生理周期记录模型
/// 生理期记录数据模型
class MenstrualRecord {
  /// 唯一标识符
  final String id;
  
  /// 开始日期
  final DateTime startDate;
  
  /// 结束日期
  final DateTime? endDate;
  
  /// 经期长度(天数)
  final int? duration;
  
  /// 经血量(1-5,1最少,5最多)
  final int? flowLevel;
  
  /// 疼痛程度(1-5,1最轻,5最重)
  final int? painLevel;
  
  /// 备注
  final String notes;
  
  /// 创建时间
  final DateTime createdAt;
  
  /// 更新时间
  final DateTime updatedAt;
  
  // 构造函数、fromMap、toMap、newRecord、copyWith方法...
}

3.2 存储服务实现

为了适配HarmonyOS平台,我们采用了内存存储方案(临时解决方案),避免了使用平台特定的存储插件如shared_preferences。

/// 生理期存储服务类,用于管理生理期记录的存储和检索
class MenstrualStorageService {
  /// 内存存储(临时解决方案,适用于鸿蒙平台)
  List<MenstrualRecord> _inMemoryStorage = [];
  
  /// 保存所有生理期记录
  Future<void> saveRecords(List<MenstrualRecord> records) async {
    _inMemoryStorage = List.from(records);
  }
  
  /// 获取所有生理期记录
  Future<List<MenstrualRecord>> getRecords() async {
    return List.from(_inMemoryStorage);
  }
  
  // 其他存储方法...
}

3.3 状态管理设计

使用Provider进行状态管理,实现了MenstrualProvider类来管理生理周期相关的状态和业务逻辑。

/// 生理期提供者,用于管理生理期记录的状态
class MenstrualProvider with ChangeNotifier {
  final MenstrualStorageService _storageService = MenstrualStorageService();
  List<MenstrualRecord> _records = [];
  bool _isLoading = false;
  
  // 设置项
  int _averageCycleLength = 28; // 平均周期长度(天)
  int _averagePeriodLength = 5; // 平均经期长度(天)
  
  // 状态获取方法...
  
  /// 加载所有生理期记录
  Future<void> loadRecords() async {
    _isLoading = true;
    notifyListeners();
    
    try {
      _records = await _storageService.getRecords();
      // 计算平均周期和经期长度
      _calculateAverageLengths();
    } catch (e) {
      debugPrint('加载生理期记录失败: $e');
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }
  
  // 其他状态管理方法...
}

3.4 生理周期计算逻辑

3.4.1 平均周期和经期长度计算
/// 计算平均周期和经期长度
void _calculateAverageLengths() {
  if (_records.length < 2) {
    return;
  }
  
  // 按日期排序
  final sortedRecords = List.from(_records)..sort((a, b) => a.startDate.compareTo(b.startDate));
  
  // 计算周期长度总和
  int cycleSum = 0;
  int cycleCount = 0;
  for (int i = 1; i < sortedRecords.length; i++) {
    final current = sortedRecords[i];
    final previous = sortedRecords[i - 1];
    final cycleLength = current.startDate.difference(previous.startDate).inDays;
    cycleSum += cycleLength as int;
    cycleCount++;
  }
  
  // 计算平均周期长度
  if (cycleCount > 0) {
    _averageCycleLength = (cycleSum / cycleCount).round() as int;
  }
  
  // 计算经期长度总和
  int periodSum = 0;
  int periodCount = 0;
  for (final record in sortedRecords) {
    if (record.duration != null) {
      periodSum += record.duration! as int;
      periodCount++;
    }
  }
  
  // 计算平均经期长度
  if (periodCount > 0) {
    _averagePeriodLength = (periodSum / periodCount).round() as int;
  }
}
3.4.2 生理周期预测算法

周期预测逻辑流程图:

开始

是否有历史记录?

返回空

获取最近的生理记录

计算平均周期长度

预测下次生理期: 最近记录开始日期 + 平均周期长度

预测排卵期: 下次生理期 - 14天

预测易孕期: 排卵期前5天到排卵后1天

返回预测结果

结束

核心预测代码:

/// 预测下一次生理期
Future<DateTime?> predictNextPeriod() async {
  final latestRecord = await getLatestRecord();
  if (latestRecord == null) {
    return null;
  }
  
  return latestRecord.startDate.add(Duration(days: _averageCycleLength));
}

/// 预测排卵期
Future<DateTime?> predictOvulation() async {
  final latestRecord = await getLatestRecord();
  if (latestRecord == null) {
    return null;
  }
  
  // 排卵期通常在下次生理期前14天
  final nextPeriod = latestRecord.startDate.add(Duration(days: _averageCycleLength));
  return nextPeriod.subtract(const Duration(days: 14));
}

/// 预测易孕期
Future<(DateTime, DateTime)?> predictFertileWindow() async {
  final ovulation = await predictOvulation();
  if (ovulation == null) {
    return null;
  }
  
  // 易孕期通常在排卵前5天到排卵后1天
  final start = ovulation.subtract(const Duration(days: 5));
  final end = ovulation.add(const Duration(days: 1));
  return (start, end);
}

3.5 日历视图实现

3.5.1 简化日历视图设计

采用网格布局实现简化的日历视图,支持月份切换和日期选择,并在日历上标记生理周期记录。

/// 构建简化的日历视图
Widget _buildSimpleCalendar() {
  return SizedBox(
    height: 300,
    child: GridView.builder(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 7,
        childAspectRatio: 1.5,
      ),
      itemCount: 42, // 6周
      itemBuilder: (context, index) {
        // 计算日期
        final DateTime firstDayOfMonth = DateTime(_selectedDate.year, _selectedDate.month, 1);
        final int dayOfWeek = firstDayOfMonth.weekday;
        final DateTime calendarStartDate = firstDayOfMonth.subtract(Duration(days: dayOfWeek - 1));
        final DateTime cellDate = calendarStartDate.add(Duration(days: index));
        
        // 检查是否是当前月份
        final bool isCurrentMonth = cellDate.month == _selectedDate.month;
        
        // 检查是否是今天
        final bool isToday = cellDate.isAtSameMomentAs(DateTime.now());
        
        // 检查是否有生理期记录
        final bool hasRecord = false; // 这里需要根据实际记录判断
        
        return GestureDetector(
          onTap: () {
            setState(() {
              _selectedDate = cellDate;
            });
          },
          child: Container(
            margin: const EdgeInsets.all(2),
            decoration: BoxDecoration(
              color: isToday ? Colors.blue : isCurrentMonth ? Colors.white : Colors.grey[100],
              borderRadius: BorderRadius.circular(8),
              border: Border.all(
                color: _selectedDate.isAtSameMomentAs(cellDate) ? Colors.blue : Colors.transparent,
                width: 2,
              ),
            ),
            child: Stack(
              alignment: Alignment.center,
              children: [
                Text(
                  cellDate.day.toString(),
                  style: TextStyle(
                    color: isCurrentMonth ? Colors.black : Colors.grey,
                    fontWeight: isToday ? FontWeight.bold : FontWeight.normal,
                  ),
                ),
                if (hasRecord) 
                  Positioned(
                    bottom: 2,
                    child: Container(
                      width: 8,
                      height: 8,
                      decoration: BoxDecoration(
                        color: Colors.red,
                        borderRadius: BorderRadius.circular(4),
                      ),
                    ),
                  ),
              ],
            ),
          ),
        );
      },
    ),
  );
}
3.5.2 预测信息卡片

在主页显示预测信息卡片,包括下次生理期、排卵期和易孕期的预测结果。

/// 构建预测信息卡片
Widget _buildPredictionCard(MenstrualProvider provider) {
  return Card(
    elevation: 2,
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
    child: Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            '预测信息',
            style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 16),
          
          // 预测下一次生理期
          FutureBuilder<DateTime?>(
            future: provider.predictNextPeriod(),
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return _buildPredictionItem(
                  icon: Icons.calendar_month,
                  title: '下次生理期',
                  date: snapshot.data!,
                  color: Colors.red,
                );
              }
              return _buildEmptyPrediction(
                icon: Icons.calendar_month,
                title: '下次生理期',
                message: '请先记录一次生理期',
              );
            },
          ),
          // 预测排卵期和易孕期...
        ],
      ),
    ),
  );
}

3.6 添加/编辑记录页面

提供表单页面,支持添加和编辑生理周期记录,包括日期选择、经血量和疼痛程度的滑块选择等。

// 核心表单代码片段
Column(
  children: [
    // 开始日期选择
    ListTile(
      title: const Text('开始日期'),
      subtitle: Text(_formatDate(_startDate)),
      trailing: IconButton(
        icon: const Icon(Icons.calendar_today),
        onPressed: () => _showDatePicker(true),
      ),
    ),
    // 结束日期选择
    ListTile(
      title: const Text('结束日期'),
      subtitle: Text(_endDate != null ? _formatDate(_endDate!) : '未结束'),
      trailing: IconButton(
        icon: const Icon(Icons.calendar_today),
        onPressed: () => _showDatePicker(false),
      ),
    ),
    // 经血量滑块
    ListTile(
      title: const Text('经血量'),
      subtitle: Text(_flowLevel.toString()),
      trailing: SizedBox(
        width: 150,
        child: Slider(
          value: (_flowLevel ?? 3).toDouble(),
          min: 1,
          max: 5,
          divisions: 4,
          label: _flowLevel.toString(),
          onChanged: (value) {
            setState(() {
              _flowLevel = value.round();
            });
          },
        ),
      ),
    ),
    // 疼痛程度滑块
    ListTile(
      title: const Text('疼痛程度'),
      subtitle: Text(_painLevel.toString()),
      trailing: SizedBox(
        width: 150,
        child: Slider(
          value: (_painLevel ?? 3).toDouble(),
          min: 1,
          max: 5,
          divisions: 4,
          label: _painLevel.toString(),
          onChanged: (value) {
            setState(() {
              _painLevel = value.round();
            });
          },
        ),
      ),
    ),
    // 备注输入
    TextField(
      controller: _notesController,
      decoration: const InputDecoration(
        labelText: '备注',
        border: OutlineInputBorder(),
      ),
      maxLines: 3,
    ),
    // 保存按钮
    SizedBox(
      width: double.infinity,
      child: ElevatedButton.icon(
        onPressed: () => _saveRecord(),
        icon: const Icon(Icons.save),
        label: const Text('保存'),
      ),
    ),
  ],
)

四、总结

4.1 应用特点

  1. 跨平台兼容:基于Flutter框架开发,完美适配HarmonyOS平台,同时支持Android、iOS等其他平台。
  2. 直观的日历视图:采用网格布局实现的日历视图,直观展示生理周期记录和预测。
  3. 智能的周期计算:基于历史记录自动计算平均周期和经期长度,预测下次生理期、排卵期和易孕期。
  4. 全面的记录管理:支持添加、编辑和删除生理周期记录,记录经血量、疼痛程度等详细信息。
  5. 个性化设置:支持自定义平均周期和经期长度,满足不同用户的需求。
  6. 清晰的统计分析:展示周期趋势和身体状况分析,帮助用户更好地了解自己的身体。

4.2 开发经验

  1. HarmonyOS适配:为了适配HarmonyOS平台,我们采用了内存存储方案,避免了使用平台特定的存储插件,提高了应用的兼容性。
  2. 状态管理设计:使用Provider进行状态管理,实现了清晰的状态分层和数据流,提高了代码的可维护性和扩展性。
  3. UI设计原则:遵循Material Design 3设计原则,采用卡片式布局、清晰的颜色对比和直观的图标,提高了应用的用户体验。
  4. 代码质量:严格遵循Dart语言规范和最佳实践,添加了详细的文档注释,提高了代码的可读性和可维护性。

4.3 展望

  1. 数据持久化:实现基于HarmonyOS本地存储的持久化方案,替代当前的内存存储,确保数据不会丢失。
  2. 更智能的预测算法:引入机器学习算法,基于更多的生理指标和生活习惯数据,提高周期预测的准确性。
  3. 健康建议:根据用户的生理周期数据,提供个性化的健康建议和提醒。
  4. 社区功能:添加社区功能,允许用户分享经验和交流健康知识。
  5. 多平台同步:实现云端同步功能,支持多设备数据同步和备份。

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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐