Flutter三方库适配OpenHarmony【mood_journal】心情日记项目完整实战

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 技术目标

本文围绕真实源码拆解以下内容:

  1. Flutter 应用入口和粉色 Material 3 主题。
  2. MoodEntry 模型如何保存 emoji、心情、备注和日期。
  3. _entries 如何保存心情记录列表。
  4. _moodOptions 如何组织 8 种心情选项。
  5. _addEntry 如何通过弹窗新增记录。
  6. StatefulBuilder 如何管理弹窗内部选中状态。
  7. _getMoodColor 如何为心情映射颜色。
  8. _getAverageMood 的真实含义为什么是积极情绪占比。
  9. _getWeekCount 如何计算最近 7 天记录数。
  10. _formatDate 如何输出 Today、Yesterday 或日期。
  11. 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 提供 MaterialAppScaffoldAppBarAlertDialogFloatingActionButtonListViewCard 等组件。

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 弹窗验证

弹窗要验证:

  1. 点击 FAB 能打开弹窗。
  2. 默认选中 Okay。
  3. 点击其他心情后选中样式变化。
  4. 备注输入框能正常输入多行文本。
  5. 点击 Save 后列表新增记录。
  6. 点击 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 种心情选项,用 AlertDialogStatefulBuilder 实现弹窗选择,用 _entries.insert(0, ...) 让最新记录置顶,用 _getMoodColor 做心情颜色映射,并在记录数达到 3 条后展示轻量统计卡片。

从 OpenHarmony 适配角度看,这个项目适合验证 Flutter 空状态、悬浮按钮、弹窗、弹窗局部状态、多行输入、emoji 文本、动态列表、统计卡片和日期格式化。排查路径也很清楚:记录不新增看 Save 分支和 setState,选中态不刷新看 StatefulBuilder,统计不准看 _getAverageMood_getWeekCount_getBestMood,日期显示异常看 _formatDate

掌握这个项目后,可以继续扩展持久化、心情评分、月历视图、趋势统计、标签分类和导出能力,让心情日记从内存 Demo 演进为更完整的跨平台记录工具。

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!


相关资源:

相关推荐
风华圆舞2 小时前
一个 Flutter 项目同时保留 Android、iOS、HarmonyOS 支持的实践
android·flutter·ios
G_dou_2 小时前
Flutter三方库适配OpenHarmony【habit_tracker】习惯追踪器项目完整实战
flutter·harmonyos
Swift社区2 小时前
鸿蒙游戏自动测试:AI 驱动的测试方案实战
人工智能·游戏·harmonyos
Swift社区2 小时前
鸿蒙 PC 性能监控:原理分析 + 实战工具
harmonyos
阿钱真强道2 小时前
27 鸿蒙LiteOS RK2206 设备连路由器完整实战(配网+联网验证+网络连通性测试)
wifi·harmonyos·鸿蒙·rk·liteos·开源鸿蒙·瑞芯微
yuegu7772 小时前
HarmonyOS应用<节气通>开发第18篇:关于页面与隐私政策
华为·harmonyos
风华圆舞2 小时前
Flutter 项目接入 HarmonyOS 的完整工程结构解析
flutter·华为·harmonyos
●VON2 小时前
AtomGit Flutter鸿蒙客户端:通知系统
flutter·华为·跨平台·harmonyos·鸿蒙