Flutter三方库适配OpenHarmony【mood_journal】心情日记项目完整实战
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
mood_journal 是一个基于 Flutter 的心情日记项目,核心代码位于 lib/main.dart。项目使用 MoodEntry 模型保存心情 emoji、心情名称、备注和记录时间,用户可以通过右下角 FloatingActionButton 打开心情选择弹窗,从 8 种心情中选择一种并填写可选备注。保存后的记录会插入列表顶部;当记录数量达到 3 条及以上时,页面会展示统计卡片,包括积极情绪占比、本周记录数和最佳心情展示。
这个项目适合讲解 Flutter 记录类应用在 OpenHarmony 上的适配过程。它覆盖了 数据模型默认时间 、空状态设计 、AlertDialog 表单弹窗 、StatefulBuilder 弹窗局部状态 、Wrap 自适应心情选项 、列表顶部插入 、日期格式化 、简单统计计算 和 Material 3 卡片列表。

图片说明:本文围绕 Flutter 弹窗、列表、日期和 OpenHarmony 承载工程展开,所有关键代码均来自 mood_journal 的真实源码。
情绪记录类应用要尤其诚实地表达数据含义。当前项目做的是轻量记录和简单统计,不是心理评估,也不是情绪诊断工具。
一、项目背景与目标
1.1 项目定位
mood_journal 是一个轻量心情记录工具。用户点击加号后,选择当前心情并输入备注,保存后在列表中看到记录。页面在没有记录时显示空状态;记录达到 3 条后展示一个统计卡片,帮助用户快速看到近期记录概况。
当前项目真实支持的功能包括:
- 默认记录列表为空。
- 空状态显示表情、提示标题和操作引导。
- 通过
FloatingActionButton打开心情弹窗。 - 弹窗内展示 8 种心情选项。
- 默认选中
Okay。 - 支持填写可选备注。
- 保存后将新记录插入列表顶部。
- 每条记录展示 emoji、心情名称、备注和日期。
- 备注为空时不显示备注文本。
- 心情名称映射不同颜色。
- 记录达到 3 条后显示统计卡片。
- 统计卡片展示积极情绪占比。
- 统计卡片展示最近 7 天记录数。
- 统计卡片展示最佳心情简化结果。
- 日期显示支持 Today、Yesterday 和完整日期。
1.2 技术目标
本文围绕真实源码拆解以下内容:
- Flutter 应用入口和粉色 Material 3 主题。
MoodEntry模型如何保存 emoji、心情、备注和日期。_entries如何保存心情记录列表。_moodOptions如何组织 8 种心情选项。_addEntry如何通过弹窗新增记录。StatefulBuilder如何管理弹窗内部选中状态。_getMoodColor如何为心情映射颜色。_getAverageMood的真实含义为什么是积极情绪占比。_getWeekCount如何计算最近 7 天记录数。_formatDate如何输出 Today、Yesterday 或日期。- OpenHarmony 侧如何验证弹窗、输入、列表和统计卡片。
1.3 核心实现速览
| 能力 | 当前实现 | 适配关注点 |
|---|---|---|
| 应用入口 | runApp(const MoodJournalApp()) |
确认首屏加载 |
| 主题 | ColorScheme.fromSeed(seedColor: Colors.pink) |
确认粉色 Material 3 样式 |
| 数据模型 | MoodEntry |
确认 emoji、mood、note、date |
| 空状态 | _entries.isEmpty |
确认无记录提示 |
| 新增入口 | FloatingActionButton |
确认弹窗打开 |
| 心情选项 | _moodOptions |
确认 8 种选项 |
| 弹窗状态 | StatefulBuilder |
确认选中态刷新 |
| 列表插入 | _entries.insert(0, ...) |
确认最新记录置顶 |
| 统计卡片 | _entries.length >= 3 |
确认条件展示 |
| 日期格式 | _formatDate |
确认 Today 和 Yesterday |
二、环境准备与工程结构
2.1 工程结构
项目保持 Flutter 标准结构,同时包含 OpenHarmony 平台工程。
| 文件或目录 | 作用 |
|---|---|
lib/main.dart |
应用入口、心情模型、弹窗、列表和统计 |
pubspec.yaml |
SDK 约束、Flutter 依赖和 Material 图标配置 |
analysis_options.yaml |
Flutter lint 规则 |
test/ |
Flutter 测试目录 |
ohos/ |
OpenHarmony 平台承载工程 |
README.md |
项目说明文件 |
当前项目没有数据库、网络请求和状态管理框架,所有记录都存在内存列表中。
2.2 依赖配置
项目使用 Dart SDK ^3.9.2,依赖 Flutter SDK。
yaml
environment:
sdk: ^3.9.2
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
flutter:
uses-material-design: true
弹窗、列表、卡片、文本输入和统计展示都由 Flutter Material 基础组件完成。
2.3 常用命令
bash
flutter pub get
flutter analyze
flutter test
flutter run
| 命令 | 用途 |
|---|---|
flutter pub get |
获取依赖 |
flutter analyze |
执行静态分析 |
flutter test |
执行测试 |
flutter run |
在目标设备运行 |
OpenHarmony 调试时,还需要结合本地 Flutter OpenHarmony 工具链完成构建、安装和运行。
三、应用入口与主题配置
3.1 import 依赖
项目只引入 Flutter Material。
dart
import 'package:flutter/material.dart';
material.dart 提供 MaterialApp、Scaffold、AppBar、AlertDialog、FloatingActionButton、ListView、Card 等组件。
3.2 main 函数
入口函数启动根组件。
dart
void main() {
runApp(const MoodJournalApp());
}
3.3 MoodJournalApp
根组件创建 MaterialApp。
dart
class MoodJournalApp extends StatelessWidget {
const MoodJournalApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Mood Journal',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.pink),
useMaterial3: true,
),
home: const MoodJournalHomePage(title: 'Mood Journal'),
);
}
}
这段代码包含三个关键点:
- 应用标题为
Mood Journal。 - 使用粉色作为主题种子色。
- 首页为
MoodJournalHomePage。
四、MoodEntry 数据模型
4.1 模型源码
项目定义了 MoodEntry 模型。
dart
class MoodEntry {
String emoji;
String mood;
String note;
DateTime date;
MoodEntry({
required this.emoji,
required this.mood,
this.note = '',
DateTime? date,
}) : date = date ?? DateTime.now();
}
4.2 字段说明
| 字段 | 类型 | 作用 |
|---|---|---|
emoji |
String |
心情图标 |
mood |
String |
心情名称 |
note |
String |
可选备注 |
date |
DateTime |
记录时间 |
4.3 默认备注
备注默认是空字符串。
dart
this.note = '',
这意味着用户可以只选择心情,不填写备注。
4.4 默认日期
日期默认是当前时间。
dart
DateTime? date,
}) : date = date ?? DateTime.now();
新增记录时没有传入日期,因此每条记录都会使用保存时的时间。
4.5 模型边界
当前字段是可变字段。
dart
String emoji;
String mood;
String note;
DateTime date;
在单页内存 Demo 中足够使用。正式项目中可以改为不可变模型,并增加序列化能力。
五、页面状态与心情选项
5.1 _entries 列表
心情记录保存在 _entries。
dart
final List<MoodEntry> _entries = [];
初始为空,因此页面首次打开会显示空状态。
5.2 _moodOptions
项目定义了 8 种心情选项。
dart
final List<Map<String, String>> _moodOptions = [
{'emoji': '😄', 'mood': 'Happy'},
{'emoji': '😊', 'mood': 'Good'},
{'emoji': '😐', 'mood': 'Okay'},
{'emoji': '😔', 'mood': 'Sad'},
{'emoji': '😢', 'mood': 'Upset'},
{'emoji': '😤', 'mood': 'Angry'},
{'emoji': '😰', 'mood': 'Anxious'},
{'emoji': '😴', 'mood': 'Tired'},
];
5.3 心情选项表
| emoji | mood |
|---|---|
| 😄 | Happy |
| 😊 | Good |
| 😐 | Okay |
| 😔 | Sad |
| 😢 | Upset |
| 😤 | Angry |
| 😰 | Anxious |
| 😴 | Tired |
这些选项覆盖积极、中性、低落、生气、焦虑和疲惫等常见状态。
六、新增心情弹窗
6.1 _addEntry 方法
新增记录通过弹窗完成。
dart
void _addEntry() async {
String selectedMood = 'Okay';
final noteController = TextEditingController();
await showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setDialogState) => AlertDialog(
title: const Text('How are you feeling?'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [],
),
actions: [],
),
),
);
}
默认选中 Okay,备注输入由 noteController 管理。
6.2 AlertDialog
弹窗标题是 How are you feeling?。
dart
AlertDialog(
title: const Text('How are you feeling?'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [],
),
)
mainAxisSize: MainAxisSize.min 让弹窗高度随内容收缩。
6.3 心情选项 Wrap
心情选项使用 Wrap 渲染。
dart
Wrap(
spacing: 8,
runSpacing: 8,
children: _moodOptions.map((option) {
final isSelected = selectedMood == option['mood'];
return GestureDetector(
onTap: () => setDialogState(() => selectedMood = option['mood']!),
child: Container(
padding: const EdgeInsets.all(12),
),
);
}).toList(),
)
Wrap 可以在弹窗宽度有限时自动换行。
6.4 选中样式
选中项使用粉色浅背景和粉色边框。
dart
decoration: BoxDecoration(
color: isSelected ? Colors.pink.shade50 : Colors.grey.shade100,
border: Border.all(
color: isSelected ? Colors.pink : Colors.transparent,
width: 2,
),
borderRadius: BorderRadius.circular(12),
)
这让用户能明确看到当前选择。
七、备注输入与保存
7.1 备注输入框
弹窗下方提供备注输入。
dart
TextField(
controller: noteController,
maxLines: 3,
decoration: const InputDecoration(
labelText: 'Add a note (optional)',
border: OutlineInputBorder(),
),
)
maxLines: 3 适合输入短备注。
7.2 Cancel 按钮
取消按钮只关闭弹窗。
dart
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
)
7.3 Save 按钮
保存按钮先找到选中的心情数据,再插入列表顶部。
dart
ElevatedButton(
onPressed: () {
final moodData = _moodOptions.firstWhere((o) => o['mood'] == selectedMood);
setState(() {
_entries.insert(0, MoodEntry(
emoji: moodData['emoji']!,
mood: moodData['mood']!,
note: noteController.text,
));
});
Navigator.pop(context);
},
child: const Text('Save'),
)
7.4 插入顶部
dart
_entries.insert(0, MoodEntry(...));
最新记录会显示在列表最上方,符合日记类应用的阅读习惯。
八、空状态设计
8.1 空状态条件
当 _entries 为空时显示空状态。
dart
body: _entries.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [],
),
)
: Column(children: [])
8.2 空状态内容
dart
const Text('😊', style: TextStyle(fontSize: 64))
dart
const Text(
'No mood entries yet',
style: TextStyle(fontSize: 18, color: Colors.grey),
)
dart
const Text(
'Tap + to add how you\'re feeling',
style: TextStyle(color: Colors.grey),
)
空状态包含表情、标题和明确操作引导。
8.3 状态切换
| 状态 | 条件 | 页面 |
|---|---|---|
| 无记录 | _entries.isEmpty |
显示空状态 |
| 有记录但少于 3 条 | _entries.length < 3 |
只显示列表 |
| 3 条及以上 | _entries.length >= 3 |
显示统计卡片和列表 |
九、心情颜色映射
9.1 _getMoodColor
不同心情映射不同颜色。
dart
Color _getMoodColor(String mood) {
switch (mood) {
case 'Happy':
case 'Good':
return Colors.green;
case 'Okay':
return Colors.yellow.shade700;
case 'Sad':
case 'Upset':
return Colors.blue;
case 'Angry':
return Colors.red;
case 'Anxious':
return Colors.orange;
case 'Tired':
return Colors.purple;
default:
return Colors.grey;
}
}
9.2 映射表
| mood | color |
|---|---|
| Happy | green |
| Good | green |
| Okay | yellow |
| Sad | blue |
| Upset | blue |
| Angry | red |
| Anxious | orange |
| Tired | purple |
| 其他 | grey |
颜色用于列表卡片中的背景和心情名称文字。
9.3 颜色使用位置
dart
color: _getMoodColor(entry.mood).withValues(alpha: 0.2)
dart
color: _getMoodColor(entry.mood)
前者用于 emoji 容器浅色背景,后者用于心情名称文字。
十、记录列表展示
10.1 ListView.builder
有记录时,列表通过 ListView.builder 渲染。
dart
ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: _entries.length,
itemBuilder: (context, index) {
final entry = _entries[index];
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(children: []),
),
);
},
)
10.2 emoji 区域
每条记录左侧是 60 x 60 的圆角容器。
dart
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: _getMoodColor(entry.mood).withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Text(entry.emoji, style: const TextStyle(fontSize: 32)),
),
)
浅色背景让 emoji 更突出。
10.3 文本区域
右侧展示心情名称、备注和日期。
dart
Text(
entry.mood,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: _getMoodColor(entry.mood),
),
)
备注非空时才显示。
dart
if (entry.note.isNotEmpty)
Text(
entry.note,
style: const TextStyle(color: Colors.grey),
maxLines: 2,
overflow: TextOverflow.ellipsis,
)
10.4 日期文本
dart
Text(
_formatDate(entry.date),
style: const TextStyle(fontSize: 12, color: Colors.grey),
)
日期通过 _formatDate 转为更友好的文本。
十一、统计卡片
11.1 显示条件
记录数达到 3 条后显示统计卡片。
dart
if (_entries.length >= 3)
Card(
margin: const EdgeInsets.all(16),
color: Colors.pink.shade50,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatColumn('Average Mood', _getAverageMood()),
_buildStatColumn('This Week', '${_getWeekCount()} entries'),
_buildStatColumn('Best Day', _getBestMood()),
],
),
),
)
11.2 _buildStatColumn
统计列封装为方法。
dart
Widget _buildStatColumn(String label, String value) {
return Column(
children: [
Text(label, style: const TextStyle(fontSize: 12, color: Colors.grey)),
const SizedBox(height: 4),
Text(value, style: const TextStyle(fontWeight: FontWeight.bold)),
],
);
}
11.3 三个统计项
| 标签 | 数据来源 | 真实含义 |
|---|---|---|
| Average Mood | _getAverageMood() |
Happy 或 Good 占比 |
| This Week | _getWeekCount() |
最近 7 天记录数 |
| Best Day | _getBestMood() |
有 Happy 则显示固定 smiling emoji,否则显示最新记录 emoji |
这里的 Average Mood 不是严格的平均分,而是积极情绪记录占比。
十二、统计方法拆解
12.1 _getAverageMood
dart
String _getAverageMood() {
if (_entries.isEmpty) return '-';
final happyCount = _entries.where((e) => e.mood == 'Happy' || e.mood == 'Good').length;
return '${((happyCount / _entries.length) * 100).toInt()}%';
}
该方法统计 Happy 和 Good 的数量,再除以总记录数,返回百分比。
12.2 _getWeekCount
dart
int _getWeekCount() {
final weekAgo = DateTime.now().subtract(const Duration(days: 7));
return _entries.where((e) => e.date.isAfter(weekAgo)).length;
}
该方法统计最近 7 天内的记录数。
12.3 _getBestMood
dart
String _getBestMood() {
if (_entries.isEmpty) return '-';
final happyEntries = _entries.where((e) => e.mood == 'Happy');
return happyEntries.isNotEmpty ? '😊' : _entries.first.emoji;
}
只要存在 Happy 记录,就返回固定 😊;否则返回最新一条记录的 emoji。
12.4 统计边界
这些统计方法足够轻量,但不是复杂情绪分析:
- 没有为每种心情赋分。
- 没有计算均值、中位数或趋势。
- 没有按自然周分组。
- Best Day 不是基于日期评分,而是简化展示。
十三、日期格式化
13.1 _formatDate
dart
String _formatDate(DateTime date) {
final now = DateTime.now();
final diff = now.difference(date);
if (diff.inDays == 0) return 'Today';
if (diff.inDays == 1) return 'Yesterday';
return '${date.month}/${date.day}/${date.year}';
}
13.2 格式规则
| 时间差 | 输出 |
|---|---|
| 0 天 | Today |
| 1 天 | Yesterday |
| 其他 | month/day/year |
13.3 示例
如果当前日期是 2026 年 6 月 9 日:
| 记录日期 | 输出 |
|---|---|
| 2026-06-09 | Today |
| 2026-06-08 | Yesterday |
| 2026-06-01 | 6/1/2026 |
当前实现基于 DateTime.now().difference(date).inDays,适合这个轻量项目。
十四、OpenHarmony 适配要点
14.1 基础组件验证
当前项目使用的 Flutter 组件包括:
| 组件 | 作用 | OpenHarmony 关注点 |
|---|---|---|
MaterialApp |
应用根组件 | 首屏加载 |
Scaffold |
页面骨架 | AppBar、Body、FAB |
FloatingActionButton |
新增入口 | 点击响应 |
AlertDialog |
心情弹窗 | 弹窗布局 |
StatefulBuilder |
弹窗局部状态 | 选中态刷新 |
Wrap |
心情选项换行 | 小屏布局 |
TextField |
备注输入 | 键盘、输入、换行 |
Card |
统计和记录卡片 | 圆角、颜色、间距 |
ListView.builder |
记录列表 | 动态滚动 |
14.2 空状态验证
首次打开应用应看到:
- 大号
😊。 No mood entries yet。Tap + to add how you're feeling。- 右下角加号按钮。
14.3 弹窗验证
弹窗要验证:
- 点击 FAB 能打开弹窗。
- 默认选中 Okay。
- 点击其他心情后选中样式变化。
- 备注输入框能正常输入多行文本。
- 点击 Save 后列表新增记录。
- 点击 Cancel 后不新增记录。
14.4 统计卡片验证
统计卡片只在 3 条及以上记录时出现。OpenHarmony 上应确认:
- 第 1 条和第 2 条记录时不显示统计卡片。
- 第 3 条记录保存后显示统计卡片。
- Average Mood 百分比跟随 Happy 和 Good 数量变化。
- This Week 统计最近 7 天记录。
- Best Day 展示逻辑符合源码。
心情日记的适配重点在"记录是否轻、展示是否清楚、统计含义是否准确"。不要把轻量统计解释成复杂情绪分析。
十五、测试与验证
15.1 初始页面测试
Widget 测试可以验证空状态。
dart
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('mood journal shows empty state', (tester) async {
await tester.pumpWidget(const MoodJournalApp());
expect(find.text('Mood Journal'), findsWidgets);
expect(find.text('No mood entries yet'), findsOneWidget);
expect(find.text('Tap + to add how you\\'re feeling'), findsOneWidget);
});
}
15.2 新增记录测试
可以测试点击 FAB、保存默认 Okay 记录。
dart
testWidgets('save mood entry adds item to list', (tester) async {
await tester.pumpWidget(const MoodJournalApp());
await tester.tap(find.byType(FloatingActionButton));
await tester.pumpAndSettle();
await tester.tap(find.text('Save'));
await tester.pumpAndSettle();
expect(find.text('Okay'), findsOneWidget);
expect(find.text('Today'), findsOneWidget);
});
15.3 统计显示测试
保存 3 条记录后应显示统计卡片。
dart
testWidgets('stats card appears after three entries', (tester) async {
await tester.pumpWidget(const MoodJournalApp());
for (var i = 0; i < 3; i++) {
await tester.tap(find.byType(FloatingActionButton));
await tester.pumpAndSettle();
await tester.tap(find.text('Save'));
await tester.pumpAndSettle();
}
expect(find.text('Average Mood'), findsOneWidget);
expect(find.text('This Week'), findsOneWidget);
expect(find.text('Best Day'), findsOneWidget);
});
15.4 手工验证矩阵
| 场景 | 操作 | 预期 |
|---|---|---|
| 首次打开 | 启动应用 | 显示空状态 |
| 打开弹窗 | 点击 FAB | 显示心情选择弹窗 |
| 切换心情 | 点击 Happy | 选中样式变为粉色 |
| 输入备注 | 输入多行文字 | 备注框正常显示 |
| 保存记录 | 点击 Save | 新记录出现在顶部 |
| 备注为空 | 只选心情保存 | 列表不显示备注行 |
| 3 条记录 | 连续保存 3 条 | 显示统计卡片 |
| 日期显示 | 保存当天记录 | 显示 Today |
十六、常见问题与优化建议
16.1 为什么新记录插入列表顶部
日记类应用通常最关注最近记录。
dart
_entries.insert(0, MoodEntry(...));
插入到索引 0 后,最新记录会优先显示。
16.2 为什么使用 StatefulBuilder
弹窗里的 selectedMood 是局部状态。
dart
StatefulBuilder(
builder: (context, setDialogState) => AlertDialog(),
)
使用 StatefulBuilder 可以只刷新弹窗内部选中样式,不必刷新整个页面。
16.3 Average Mood 为什么不是平均心情
当前方法统计的是 Happy 和 Good 的占比。
dart
final happyCount = _entries.where((e) => e.mood == 'Happy' || e.mood == 'Good').length;
因此它更准确的含义是"积极情绪记录比例"。如果需要真正平均分,需要为每种心情定义分值。
16.4 Best Day 为什么是简化逻辑
当前 _getBestMood 只判断是否存在 Happy。
dart
return happyEntries.isNotEmpty ? '😊' : _entries.first.emoji;
它没有按日期计算哪一天最好。更完整的做法是按日期聚合并比较心情分值。
16.5 如何增加心情评分
可以为心情增加 score。
dart
class MoodOption {
final String emoji;
final String mood;
final int score;
const MoodOption({
required this.emoji,
required this.mood,
required this.score,
});
}
例如 Happy 为 5,Good 为 4,Okay 为 3,Sad 为 2,Upset 为 1。
16.6 如何增加持久化
当前记录只存在内存中,应用重启后会清空。可以把记录序列化到本地存储。
dart
class MoodEntryDto {
final String emoji;
final String mood;
final String note;
final String date;
const MoodEntryDto({
required this.emoji,
required this.mood,
required this.note,
required this.date,
});
}
OpenHarmony 上实现持久化时,需要结合可用插件和平台存储能力。
十七、工程扩展方向
17.1 抽取 MoodEntryCard
记录卡片可以拆成组件。
dart
class MoodEntryCard extends StatelessWidget {
final MoodEntry entry;
final Color Function(String mood) moodColorOf;
final String Function(DateTime date) formatDate;
const MoodEntryCard({
super.key,
required this.entry,
required this.moodColorOf,
required this.formatDate,
});
}
拆分后页面主体更清晰,也便于单独测试卡片。
17.2 抽取新增弹窗结果
弹窗可以返回一个结果对象。
dart
class MoodEntryInput {
final String mood;
final String emoji;
final String note;
const MoodEntryInput({
required this.mood,
required this.emoji,
required this.note,
});
}
页面接收结果后再插入 _entries。
17.3 增加月历视图
心情日记适合增加日历展示。
dart
class DailyMood {
final DateTime date;
final List<MoodEntry> entries;
const DailyMood({
required this.date,
required this.entries,
});
}
按日期聚合后,可以显示每天的代表心情。
17.4 增加趋势统计
如果为心情定义 score,可以计算趋势。
dart
double averageMoodScore(List<int> scores) {
if (scores.isEmpty) return 0;
return scores.reduce((a, b) => a + b) / scores.length;
}
趋势统计应明确是自我记录参考,不应被表述为诊断结果。
总结
mood_journal 是一个结构清晰的 Flutter 心情日记案例。它用 MoodEntry 保存 emoji、心情名称、备注和日期,用 _moodOptions 提供 8 种心情选项,用 AlertDialog 和 StatefulBuilder 实现弹窗选择,用 _entries.insert(0, ...) 让最新记录置顶,用 _getMoodColor 做心情颜色映射,并在记录数达到 3 条后展示轻量统计卡片。
从 OpenHarmony 适配角度看,这个项目适合验证 Flutter 空状态、悬浮按钮、弹窗、弹窗局部状态、多行输入、emoji 文本、动态列表、统计卡片和日期格式化。排查路径也很清楚:记录不新增看 Save 分支和 setState,选中态不刷新看 StatefulBuilder,统计不准看 _getAverageMood、_getWeekCount、_getBestMood,日期显示异常看 _formatDate。
掌握这个项目后,可以继续扩展持久化、心情评分、月历视图、趋势统计、标签分类和导出能力,让心情日记从内存 Demo 演进为更完整的跨平台记录工具。
如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!
相关资源: