鸿蒙+flutter 跨平台开发——基于日历视图的生理周期计算逻辑
本文介绍了一个基于Flutter和HarmonyOS开发的跨平台生理周期记录应用。该应用采用日历视图直观展示周期数据,提供记录管理、周期预测和统计分析功能。技术栈包括Flutter 3.16.0、Dart 3.2.0和Provider状态管理。应用架构分为UI层、状态管理层、服务层和数据层,核心功能包括数据模型设计、存储服务实现和生理周期计算逻辑。特别针对HarmonyOS平台采用了内存存储方案,
鸿蒙+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 生理周期预测算法
周期预测逻辑流程图:
核心预测代码:
/// 预测下一次生理期
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 应用特点
- 跨平台兼容:基于Flutter框架开发,完美适配HarmonyOS平台,同时支持Android、iOS等其他平台。
- 直观的日历视图:采用网格布局实现的日历视图,直观展示生理周期记录和预测。
- 智能的周期计算:基于历史记录自动计算平均周期和经期长度,预测下次生理期、排卵期和易孕期。
- 全面的记录管理:支持添加、编辑和删除生理周期记录,记录经血量、疼痛程度等详细信息。
- 个性化设置:支持自定义平均周期和经期长度,满足不同用户的需求。
- 清晰的统计分析:展示周期趋势和身体状况分析,帮助用户更好地了解自己的身体。
4.2 开发经验
- HarmonyOS适配:为了适配HarmonyOS平台,我们采用了内存存储方案,避免了使用平台特定的存储插件,提高了应用的兼容性。
- 状态管理设计:使用Provider进行状态管理,实现了清晰的状态分层和数据流,提高了代码的可维护性和扩展性。
- UI设计原则:遵循Material Design 3设计原则,采用卡片式布局、清晰的颜色对比和直观的图标,提高了应用的用户体验。
- 代码质量:严格遵循Dart语言规范和最佳实践,添加了详细的文档注释,提高了代码的可读性和可维护性。
4.3 展望
- 数据持久化:实现基于HarmonyOS本地存储的持久化方案,替代当前的内存存储,确保数据不会丢失。
- 更智能的预测算法:引入机器学习算法,基于更多的生理指标和生活习惯数据,提高周期预测的准确性。
- 健康建议:根据用户的生理周期数据,提供个性化的健康建议和提醒。
- 社区功能:添加社区功能,允许用户分享经验和交流健康知识。
- 多平台同步:实现云端同步功能,支持多设备数据同步和备份。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐




所有评论(0)