Flutter 框架跨平台鸿蒙开发 - 跟生活有关的心情日记应用开发
摘要: 心情日记是一款基于Flutter开发的跨平台情绪记录应用,支持鸿蒙OS系统。核心功能包括8种心情类型选择(配表情符号与色彩)、日记撰写(标题/内容/日期/地点/天气)、标签管理、搜索筛选及可视化心情统计。采用Material Design 3规范,使用setState进行状态管理,项目结构包含主列表页、编辑页和详情页。数据模型涵盖日记ID、标题、内容、心情类型等10个字段,通过GridVi
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
一、项目概述
运行效果图





1.1 应用简介
心情日记是一款专注于情感记录与追踪的移动应用,为用户提供一个私密、便捷的空间记录每日心情变化。在快节奏的现代生活中,人们常常忽略了对自身情绪的关注与觉察,本应用帮助用户追踪情绪轨迹,从而更好地了解自己、关爱自己。
应用支持八种心情类型选择,配以表情符号与主题色彩,让情绪表达更加直观生动。日记撰写支持标题、内容、日期、地点、天气等全方位记录,标签管理功能让日记分类更加灵活。心情统计功能可视化展示各心情占比,帮助用户洞察情绪规律。
1.2 核心功能
| 功能模块 | 功能描述 | 实现方式 |
|---|---|---|
| 心情选择 | 八种心情类型,配以表情符号与主题色彩 | GridView选择器 |
| 日记撰写 | 标题、内容、日期、地点、天气全方位记录 | 表单页面 |
| 标签管理 | 自定义标签,灵活分类日记内容 | Chip组件 |
| 搜索筛选 | 按关键词、心情类型快速定位日记 | TextField过滤 |
| 心情统计 | 可视化展示各心情占比,洞察情绪规律 | AlertDialog + 进度条 |
| 日记详情 | 沉浸式阅读体验,完整展示日记信息 | 详情页面 |
| 日记编辑 | 修改已有日记内容 | 表单预填充 |
| 日记删除 | 删除不需要的记录 | 确认对话框 |
1.3 日记记录字段
| 字段 | 类型 | 说明 |
|---|---|---|
| 日记ID | String | 唯一标识,时间戳生成 |
| 日记标题 | String | 日记名称,必填 |
| 日记内容 | String | 日记正文,必填 |
| 心情类型 | MoodType | 八种心情枚举 |
| 日记日期 | DateTime | 日记记录日期 |
| 标签列表 | List | 分类标签 |
| 地点 | String | 记录地点 |
| 天气 | String | 当日天气 |
| 创建时间 | DateTime | 记录创建时间 |
| 更新时间 | DateTime | 最后修改时间 |
1.4 技术栈
| 技术领域 | 技术选型 | 版本要求 |
|---|---|---|
| 开发框架 | Flutter | >= 3.0.0 |
| 编程语言 | Dart | >= 2.17.0 |
| 设计规范 | Material Design 3 | - |
| 状态管理 | setState | - |
| 目标平台 | 鸿蒙OS | API 21+ |
1.5 项目结构
lib/
└── main_mood_diary.dart
├── MoodDiaryApp # 应用入口
├── MoodType # 心情类型枚举
├── MoodDiary # 日记数据模型
├── MoodDiaryPage # 主列表页面
│ ├── _buildMoodSummary() # 心情概览卡片
│ ├── _buildSearchBar() # 搜索栏
│ ├── _buildDiariesList() # 日记列表
│ ├── _buildDiaryCard() # 日记卡片
│ └── _showStatistics() # 统计对话框
├── MoodDiaryEditPage # 编辑页面
│ ├── _selectDate() # 日期选择
│ ├── _addTag() # 添加标签
│ └── _save() # 保存日记
└── MoodDiaryDetailPage # 详情页面
二、系统架构
2.1 整体架构图
2.2 类图设计
2.3 数据流程图
2.4 心情统计流程
三、核心模块设计
3.1 数据模型设计
3.1.1 心情类型枚举 (MoodType)
enum MoodType {
happy, // 开心
sad, // 难过
angry, // 愤怒
anxious, // 焦虑
calm, // 平静
excited, // 兴奋
tired, // 疲惫
grateful, // 感恩
}
3.1.2 心情属性映射
| 心情类型 | 中文名称 | 表情符号 | 主题颜色 |
|---|---|---|---|
| happy | 开心 | 😊 | 黄色 |
| sad | 难过 | 😢 | 蓝色 |
| angry | 愤怒 | 😠 | 红色 |
| anxious | 焦虑 | 😰 | 紫色 |
| calm | 平静 | 😌 | 绿色 |
| excited | 兴奋 | 🤩 | 橙色 |
| tired | 疲惫 | 😴 | 灰色 |
| grateful | 感恩 | 🙏 | 粉色 |
3.1.3 日记模型 (MoodDiary)
class MoodDiary {
final String id; // 唯一标识
String title; // 日记标题
String content; // 日记内容
MoodType mood; // 心情类型
DateTime date; // 日记日期
List<String> tags; // 标签列表
String location; // 地点
String weather; // 天气
DateTime createdAt; // 创建时间
DateTime updatedAt; // 更新时间
MoodDiary({
required this.id,
required this.title,
required this.content,
required this.mood,
required this.date,
this.tags = const [],
this.location = '',
this.weather = '',
required this.createdAt,
required this.updatedAt,
});
String get moodText; // 心情中文名称
String get moodEmoji; // 心情表情符号
Color get moodColor; // 心情主题颜色
}
3.2 筛选过滤算法
3.2.1 过滤流程
3.2.2 过滤实现
List<MoodDiary> get _filteredDiaries {
var diaries = _diaries.toList();
// 心情类型过滤
if (_selectedMood != null) {
diaries = diaries.where((d) => d.mood == _selectedMood).toList();
}
// 关键词搜索
if (_searchQuery.isNotEmpty) {
diaries = diaries.where((d) {
return d.title.toLowerCase().contains(_searchQuery.toLowerCase()) ||
d.content.toLowerCase().contains(_searchQuery.toLowerCase()) ||
d.tags.any((tag) =>
tag.toLowerCase().contains(_searchQuery.toLowerCase()));
}).toList();
}
// 按日期倒序排列
diaries.sort((a, b) => b.date.compareTo(a.date));
return diaries;
}
3.3 心情统计算法
3.3.1 统计流程
3.3.2 统计实现
Map<MoodType, int> get _moodStats {
final stats = <MoodType, int>{};
for (var mood in MoodType.values) {
stats[mood] = _diaries.where((d) => d.mood == mood).length;
}
return stats;
}
// 在统计对话框中计算百分比
final count = stats[mood] ?? 0;
final total = _diaries.length;
final percentage = total > 0
? (count / total * 100).toStringAsFixed(1)
: '0.0';
3.4 今日心情计算
3.4.1 计算流程
3.4.2 计算实现
Widget _buildMoodSummary() {
final today = DateTime.now();
final todayDiaries = _diaries
.where((d) =>
d.date.year == today.year &&
d.date.month == today.month &&
d.date.day == today.day)
.toList();
MoodType? todayMood;
if (todayDiaries.isNotEmpty) {
final moodCounts = <MoodType, int>{};
for (var d in todayDiaries) {
moodCounts[d.mood] = (moodCounts[d.mood] ?? 0) + 1;
}
todayMood = moodCounts.entries
.reduce((a, b) => a.value > b.value ? a : b)
.key;
}
// ...
}
3.5 页面结构设计
3.5.1 列表页面布局
3.5.2 编辑页面布局
┌─────────────────────────────────────────────────────────────┐
│ AppBar: 写日记/编辑日记 [💾 保存] │
├─────────────────────────────────────────────────────────────┤
│ 今天的心情 │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ 😊 │ │ 😢 │ │ 😠 │ │ 😰 │ │ 😌 │ │ 🤩 │ │ 😴 │ │ 🙏 │ │
│ │开心│ │难过│ │愤怒│ │焦虑│ │平静│ │兴奋│ │疲惫│ │感恩│ │
│ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ └────┘ │
│ │
│ 📝 日记标题 * │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 📅 日期 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 2024-01-15 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 📍 地点 ☀️ 天气 │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ │ │ │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
│ 📋 日记内容 * │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 多行文本输入 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 标签: [朋友] [户外] [×] │
└─────────────────────────────────────────────────────────────┘
3.6 状态管理
3.6.1 核心状态变量
class _MoodDiaryPageState extends State<MoodDiaryPage> {
final List<MoodDiary> _diaries = []; // 所有日记
String _searchQuery = ''; // 搜索关键词
MoodType? _selectedMood; // 选中的心情筛选
}
class _MoodDiaryEditPageState extends State<MoodDiaryEditPage> {
late MoodType _selectedMood; // 选中的心情
late DateTime _selectedDate; // 选中的日期
late List<String> _tags; // 标签列表
}
3.6.2 状态更新流程
// 添加日记
void _addDiary() async {
final result = await Navigator.push<MoodDiary>(
context,
MaterialPageRoute(builder: (context) => const MoodDiaryEditPage()),
);
if (result != null) {
setState(() {
_diaries.insert(0, result);
});
}
}
// 删除日记
void _deleteDiary(MoodDiary diary) {
setState(() {
_diaries.removeWhere((d) => d.id == diary.id);
});
}
四、UI设计规范
4.1 配色方案
应用采用粉色主题风格,体现温馨的情感氛围:
| 颜色类型 | 色值 | 用途 |
|---|---|---|
| 主色 | Pink | AppBar、按钮、强调 |
| 开心 | Yellow | 开心心情标识 |
| 难过 | Blue | 难过心情标识 |
| 愤怒 | Red | 愤怒心情标识 |
| 焦虑 | Purple | 焦虑心情标识 |
| 平静 | Green | 平静心情标识 |
| 兴奋 | Orange | 兴奋心情标识 |
| 疲惫 | Grey | 疲惫心情标识 |
| 感恩 | Pink | 感恩心情标识 |
4.2 心情类型样式
4.2.1 心情选择器
| 心情 | 表情 | 颜色 | 选中效果 |
|---|---|---|---|
| 开心 | 😊 | 黄色 | 黄色背景 + 黄色边框 |
| 难过 | 😢 | 蓝色 | 蓝色背景 + 蓝色边框 |
| 愤怒 | 😠 | 红色 | 红色背景 + 红色边框 |
| 焦虑 | 😰 | 紫色 | 紫色背景 + 紫色边框 |
| 平静 | 😌 | 绿色 | 绿色背景 + 绿色边框 |
| 兴奋 | 🤩 | 橙色 | 橙色背景 + 橙色边框 |
| 疲惫 | 😴 | 灰色 | 灰色背景 + 灰色边框 |
| 感恩 | 🙏 | 粉色 | 粉色背景 + 粉色边框 |
4.2.2 心情卡片图标
┌─────────────────┐
│ 😊 │ ← 心情表情
│ │
│ 彩色圆角背景 │ ← 心情对应颜色
└─────────────────┘
4.3 组件规范
4.3.1 日记卡片
┌─────────────────────────────────────────────────────────────┐
│ ┌────┐ 美好的一天 ⋮ │
│ │ 😊 │ 2024-01-15 │
│ └────┘ │
│ 今天阳光明媚,和朋友一起去了公园野餐。大家聊得很开心... │
│ │
│ 📍 中央公园 ☀️ 晴天 [朋友] [户外] │
└─────────────────────────────────────────────────────────────┘
4.3.2 心情概览卡片
┌─────────────────────────────────────────────────────────────┐
│ ┌────┐ 今日心情 ┌────┐ │
│ │ 😊 │ 开心 │ 15 │ │
│ │ │ │篇日记│ │
│ └────┘ └────┘ │
└─────────────────────────────────────────────────────────────┘
4.3.3 统计对话框
┌─────────────────────────────────────────┐
│ 心情统计 [×] │
├─────────────────────────────────────────┤
│ 😊 开心 5篇 (33.3%) │
│ ████████████░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ │
│ 😢 难过 2篇 (13.3%) │
│ █████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ │
│ 😌 平静 3篇 (20.0%) │
│ ████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ ... │
├─────────────────────────────────────────┤
│ [关闭] │
└─────────────────────────────────────────┘
4.4 交互设计
4.4.1 操作方式
| 操作 | 手势 | 效果 |
|---|---|---|
| 查看详情 | 点击卡片 | 跳转详情页 |
| 新建日记 | 点击浮动按钮 | 跳转编辑页 |
| 编辑日记 | 点击菜单-编辑 | 跳转编辑页 |
| 删除日记 | 点击菜单-删除 | 确认后删除 |
| 搜索 | 输入关键词 | 实时过滤 |
| 筛选 | 点击筛选图标 | 选择心情类型 |
| 统计 | 点击统计图标 | 显示统计对话框 |
4.4.2 心情选择交互
未选中状态: ┌────────┐
│ 😊 │ 灰色背景
│ 开心 │
└────────┘
选中状态: ┌────────┐
│ 😊 │ 黄色背景 + 黄色边框
│ 开心 │
└────────┘
五、核心功能实现
5.1 列表页面构建
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('心情日记'),
actions: [
IconButton(
icon: const Icon(Icons.bar_chart),
onPressed: _showStatistics,
),
PopupMenuButton<MoodType?>(
icon: const Icon(Icons.filter_list),
onSelected: (value) {
setState(() {
_selectedMood = value;
});
},
itemBuilder: (context) => [
const PopupMenuItem(value: null, child: Text('全部心情')),
...MoodType.values.map((mood) => PopupMenuItem(
value: mood,
child: Text(moodEmoji + ' ' + moodText),
)),
],
),
],
),
body: Column(
children: [
_buildMoodSummary(),
_buildSearchBar(),
Expanded(
child: _filteredDiaries.isEmpty
? _buildEmptyState()
: _buildDiariesList(),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _addDiary,
child: const Icon(Icons.add),
),
);
}
5.2 心情选择器
Wrap(
spacing: 12,
runSpacing: 12,
children: MoodType.values.map((mood) {
final isSelected = _selectedMood == mood;
final tempDiary = MoodDiary(
id: '', title: '', content: '',
mood: mood, date: DateTime.now(),
createdAt: DateTime.now(), updatedAt: DateTime.now(),
);
return GestureDetector(
onTap: () {
setState(() {
_selectedMood = mood;
});
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: isSelected
? tempDiary.moodColor.withValues(alpha: 0.2)
: Colors.grey.shade100,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: isSelected ? tempDiary.moodColor : Colors.transparent,
width: 2,
),
),
child: Column(
children: [
Text(tempDiary.moodEmoji, style: const TextStyle(fontSize: 28)),
const SizedBox(height: 4),
Text(tempDiary.moodText),
],
),
),
);
}).toList(),
)
5.3 标签管理
void _addTag() {
if (_tagController.text.isNotEmpty &&
!_tags.contains(_tagController.text)) {
setState(() {
_tags.add(_tagController.text);
_tagController.clear();
});
}
}
void _removeTag(String tag) {
setState(() {
_tags.remove(tag);
});
}
// 标签显示
Wrap(
spacing: 8,
runSpacing: 8,
children: _tags.map((tag) {
return Chip(
label: Text(tag),
onDeleted: () => _removeTag(tag),
deleteIcon: const Icon(Icons.close, size: 16),
);
}).toList(),
)
5.4 心情统计对话框
void _showStatistics() {
final stats = _moodStats;
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('心情统计'),
content: SizedBox(
width: 300,
child: Column(
mainAxisSize: MainAxisSize.min,
children: MoodType.values.map((mood) {
final count = stats[mood] ?? 0;
final total = _diaries.length;
final percentage = total > 0
? (count / total * 100).toStringAsFixed(1)
: '0.0';
return Row(
children: [
Text(moodEmoji, style: const TextStyle(fontSize: 24)),
const SizedBox(width: 8),
Expanded(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(moodText),
Text('$count篇 ($percentage%)'),
],
),
LinearProgressIndicator(
value: total > 0 ? count / total : 0,
backgroundColor: Colors.grey.shade200,
valueColor: AlwaysStoppedAnimation(moodColor),
),
],
),
),
],
);
}).toList(),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('关闭'),
),
],
);
},
);
}
5.5 表单验证与保存
void _save() {
if (_formKey.currentState!.validate()) {
final now = DateTime.now();
final diary = MoodDiary(
id: widget.diary?.id ?? DateTime.now().millisecondsSinceEpoch.toString(),
title: _titleController.text,
content: _contentController.text,
mood: _selectedMood,
date: _selectedDate,
location: _locationController.text,
weather: _weatherController.text,
tags: _tags,
createdAt: widget.diary?.createdAt ?? now,
updatedAt: now,
);
Navigator.pop(context, diary);
}
}
六、心情类型知识拓展
6.1 心情分类体系
6.2 心情与色彩心理学
| 心情 | 颜色 | 心理学含义 |
|---|---|---|
| 开心 | 黄色 | 温暖、活力、乐观 |
| 难过 | 蓝色 | 冷静、深沉、忧郁 |
| 愤怒 | 红色 | 热情、力量、警示 |
| 焦虑 | 紫色 | 神秘、敏感、不安 |
| 平静 | 绿色 | 自然、和谐、安宁 |
| 兴奋 | 橙色 | 活力、创意、热情 |
| 疲惫 | 灰色 | 中性、低调、休息 |
| 感恩 | 粉色 | 温柔、爱意、感激 |
6.3 心情记录的意义
七、扩展功能规划
7.1 后续版本规划
7.2 功能扩展建议
7.2.1 数据持久化
| 功能 | 说明 |
|---|---|
| SQLite存储 | 本地数据库存储日记 |
| 数据备份 | 支持数据导出备份 |
| 数据恢复 | 支持数据导入恢复 |
7.2.2 心情趋势分析
| 功能 | 说明 |
|---|---|
| 折线图 | 展示心情变化趋势 |
| 热力图 | 展示心情分布密度 |
| 月度报告 | 生成心情分析报告 |
7.2.3 智能功能
| 功能 | 说明 |
|---|---|
| 心情提醒 | 定时提醒记录心情 |
| AI分析 | 智能情绪识别建议 |
| 关联分析 | 分析心情与天气/地点关联 |
八、注意事项
8.1 开发注意事项
-
心情颜色处理:使用
withValues(alpha:)替代已废弃的withOpacity() -
日期格式化:统一使用
yyyy-MM-dd格式 -
表单验证:标题和内容为必填字段
-
状态更新:使用 setState 触发界面刷新
8.2 用户体验优化
💡 用户体验建议 💡
- 心情选择直观便捷
- 日记记录简洁高效
- 统计展示清晰明了
- 搜索筛选快速精准
8.3 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 心情颜色不显示 | 颜色值错误 | 检查moodColor getter |
| 统计百分比错误 | 总数为0 | 添加总数判断 |
| 搜索无结果 | 过滤条件错误 | 检查过滤逻辑 |
| 日期显示异常 | 格式化错误 | 统一日期格式 |
九、运行说明
9.1 环境要求
| 环境 | 版本要求 |
|---|---|
| Flutter SDK | >= 3.0.0 |
| Dart SDK | >= 2.17.0 |
| 鸿蒙OS | API 21+ |
9.2 运行命令
# 查看可用设备
flutter devices
# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 lib/main_mood_diary.dart
# 运行到Windows
flutter run -d windows -t lib/main_mood_diary.dart
# 代码分析
flutter analyze lib/main_mood_diary.dart
十、总结
心情日记应用通过完善的功能设计,帮助用户记录和管理每日心情。应用支持八种心情类型,配以表情符号与主题色彩,让情绪表达更加直观生动。日记撰写支持标题、内容、日期、地点、天气等全方位记录,标签管理功能让日记分类更加灵活。
心情统计功能可视化展示各心情占比,帮助用户洞察情绪规律。今日心情功能自动计算当天主要心情状态,让用户快速了解当日情绪。搜索和筛选功能让用户快速定位目标日记,提高管理效率。
界面设计采用粉色主题风格,体现温馨的情感氛围。心情选择器采用彩色卡片设计,选中状态清晰明了。应用采用Material Design 3设计规范,遵循Flutter最佳实践,代码结构清晰,易于维护和扩展。
更多推荐




所有评论(0)