Flutter for OpenHarmony生活助手App实战:心情日记功能实现
本文介绍了一个生活助手App中的心情日记功能设计与实现。作者阐述了记录心情的三个价值:情绪管理、心理疏导和自我认知。技术实现上采用Flutter框架,页面设计简洁温暖,包含5种表情选择、日记输入框和保存功能。关键实现包括StatefulWidget状态管理、Wrap布局自动换行、SharedPreferences本地存储等。文章强调了用户体验细节,如表情符号比图标更生动、文字标签消除歧义、大按钮易

说起记录心情,我以前觉得这是个很矫情的事情。但后来发现,定期记录自己的情绪变化,对心理健康真的很有帮助。有时候回头看看以前的心情记录,会发现很多当时觉得天大的事情,现在看来也就那样。所以在做这个生活助手App时,我特别想加入心情日记这个功能。
为什么要记录心情
在开始写代码之前,我先想清楚了这个功能的价值。
第一个是情绪管理。很多人不知道自己为什么会突然心情不好,通过记录心情,可以发现一些规律。比如每周一心情都不太好,可能是工作压力大;每次和某个人聊天后心情变差,可能是这段关系有问题。
第二个是心理疏导。把心情写下来,本身就是一种释放。有些话不方便跟别人说,但可以写在日记里。写完之后,心情往往会好一些。
第三个是自我认知。通过长期记录,可以更了解自己。什么事情会让自己开心,什么事情会让自己难过,这些都是很重要的自我认知。
页面整体设计
心情日记的页面设计要简洁温暖,不能太冷冰冰。我用了柔和的颜色和圆润的卡片:
class MoodTrackerPage extends StatefulWidget {
const MoodTrackerPage({super.key});
State<MoodTrackerPage> createState() => _MoodTrackerPageState();
}
class _MoodTrackerPageState extends State<MoodTrackerPage> {
String? selectedMood;
final List<Map<String, dynamic>> moods = [
{'emoji': '😄', 'label': '很开心', 'color': Colors.green},
{'emoji': '🙂', 'label': '开心', 'color': Colors.lightGreen},
{'emoji': '😐', 'label': '一般', 'color': Colors.orange},
{'emoji': '😔', 'label': '难过', 'color': Colors.deepOrange},
{'emoji': '😢', 'label': '很难过', 'color': Colors.red},
];
为什么用StatefulWidget
这里必须用StatefulWidget,因为用户选择心情后,界面要更新。selectedMood这个变量存储用户选择的心情,初始值是null,表示还没选择。
心情数据的设计
我定义了5种心情,从很开心到很难过。每种心情包含三个属性:
emoji:表情符号,比😄、🙂这些,比图标更生动label:文字描述,让用户明确知道这是什么心情color:对应的颜色,绿色代表开心,红色代表难过
这个设计是我反复调整的结果。一开始我用了7种心情,后来发现太多了,用户选择起来反而困难。5种刚刚好,覆盖了从好到坏的主要情绪。
标题的设计
页面顶部有个温暖的标题:
body: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'今天的心情如何?',
style: TextStyle(fontSize: 20.sp, fontWeight: FontWeight.bold),
),
SizedBox(height: 24.h),
"今天的心情如何?“这个问题很亲切,像朋友在关心你。我试过很多种表达,比如"选择你的心情”、“记录今日心情”,但都没有这个好。语气要温暖,不要太官方。
心情选择器的实现
心情选择器是这个页面的核心,我用了Wrap布局:
Wrap(
spacing: 12.w,
runSpacing: 12.h,
children: moods.map((mood) {
final isSelected = selectedMood == mood['label'];
return GestureDetector(
onTap: () {
setState(() {
selectedMood = mood['label'] as String;
});
},
child: Container(
width: 100.w,
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: isSelected ? (mood['color'] as Color).withOpacity(0.2) : Colors.white,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: isSelected ? mood['color'] as Color : Colors.grey[300]!,
width: 2,
),
),
为什么用Wrap而不是Row
Wrap的好处是可以自动换行。如果屏幕宽度不够,心情卡片会自动换到下一行,不会被挤压变形。spacing是横向间距,runSpacing是纵向间距。
选中状态的视觉反馈
选中的心情卡片有两个视觉变化:
- 背景色变成对应颜色的半透明版本(
withOpacity(0.2)) - 边框变成对应颜色的实色
这种设计让用户一眼就能看出选了哪个心情。我一开始用的是实色背景,但发现表情符号看不清楚,改成半透明后效果好多了。
心情卡片的内容
每个心情卡片包含表情和文字:
child: Column(
children: [
Text(
mood['emoji'] as String,
style: TextStyle(fontSize: 40.sp),
),
SizedBox(height: 8.h),
Text(
mood['label'] as String,
style: TextStyle(fontSize: 12.sp),
),
],
),
表情符号的大小
表情用了40sp的大字号,这样很醒目。表情符号比图标更有温度,用户看到😄就会联想到开心的感觉。
文字标签的必要性
虽然表情已经很直观了,但还是加了文字标签。因为有些表情可能会有歧义,文字标签能消除这种歧义。而且对于视力不好的用户,文字标签也更友好。
日记输入框
选择心情后,用户可以写下今天发生的事情:
SizedBox(height: 24.h),
TextField(
maxLines: 5,
decoration: InputDecoration(
hintText: '记录今天发生的事情...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.r),
),
),
),
多行输入框的设置
maxLines: 5让输入框有5行高度,这样用户可以写比较长的内容。如果内容超过5行,输入框会自动滚动。
提示文字的设计
"记录今天发生的事情…“这个提示很开放,不限制用户写什么。我试过"写下让你开心/难过的事情”,但发现这样太限制了,有些用户可能只是想随便写写。
保存按钮
页面底部是一个保存按钮:
SizedBox(height: 16.h),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('心情记录已保存')),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(vertical: 16.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
),
child: Text(
'保存',
style: TextStyle(fontSize: 16.sp, color: Colors.white),
),
),
),
按钮占满整行
width: double.infinity让按钮占满整行,这样更醒目,也更容易点击。移动端的按钮要足够大,不然用户点击起来很费劲。
保存反馈
点击保存后,用SnackBar显示提示:“心情记录已保存”。这种即时反馈很重要,让用户知道操作成功了。
数据存储的实现
心情记录需要保存到本地,我用SharedPreferences:
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
class MoodRecord {
final String mood;
final String content;
final DateTime date;
MoodRecord({
required this.mood,
required this.content,
required this.date,
});
Map<String, dynamic> toJson() {
return {
'mood': mood,
'content': content,
'date': date.toIso8601String(),
};
}
factory MoodRecord.fromJson(Map<String, dynamic> json) {
return MoodRecord(
mood: json['mood'],
content: json['content'],
date: DateTime.parse(json['date']),
);
}
}
定义一个MoodRecord类来表示心情记录,包含心情、内容和日期三个字段。toJson和fromJson方法用于序列化和反序列化。
保存记录的逻辑
Future<void> saveMoodRecord(MoodRecord record) async {
final prefs = await SharedPreferences.getInstance();
// 读取已有记录
final recordsJson = prefs.getString('mood_records') ?? '[]';
final List<dynamic> recordsList = jsonDecode(recordsJson);
// 添加新记录
recordsList.insert(0, record.toJson());
// 保存回去
await prefs.setString('mood_records', jsonEncode(recordsList));
}
新记录插入到列表开头,这样最新的记录会显示在最前面。
历史记录页面
用户应该能查看过去的心情记录,我做了一个历史记录页面:
class MoodHistoryPage extends StatelessWidget {
const MoodHistoryPage({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('心情历史'),
),
body: FutureBuilder<List<MoodRecord>>(
future: loadMoodRecords(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final records = snapshot.data!;
if (records.isEmpty) {
return Center(
child: Text(
'还没有心情记录\n快去记录今天的心情吧',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16.sp, color: Colors.grey),
),
);
}
return ListView.builder(
padding: EdgeInsets.all(16.w),
itemCount: records.length,
itemBuilder: (context, index) {
return _buildRecordCard(records[index]);
},
);
},
),
);
}
}
用FutureBuilder异步加载历史记录,加载完成前显示loading,加载完成后显示列表。如果没有记录,显示一个友好的空状态提示。
历史记录卡片
Widget _buildRecordCard(MoodRecord record) {
final moodData = moods.firstWhere(
(m) => m['label'] == record.mood,
orElse: () => moods[2], // 默认返回"一般"
);
return Container(
margin: EdgeInsets.only(bottom: 12.h),
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: (moodData['color'] as Color).withOpacity(0.1),
borderRadius: BorderRadius.circular(12.r),
border: Border.all(color: moodData['color'] as Color, width: 2),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
moodData['emoji'] as String,
style: TextStyle(fontSize: 32.sp),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
moodData['label'] as String,
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
),
Text(
DateFormat('yyyy-MM-dd HH:mm').format(record.date),
style: TextStyle(fontSize: 12.sp, color: Colors.grey),
),
],
),
),
],
),
if (record.content.isNotEmpty) ...[
SizedBox(height: 12.h),
Text(
record.content,
style: TextStyle(fontSize: 14.sp),
),
],
],
),
);
}
每条记录用卡片展示,卡片的颜色和边框根据心情变化。如果有内容,就显示内容;如果没有,就只显示心情和时间。
心情统计功能
可以统计一段时间内的心情分布,用饼图展示:
Map<String, int> calculateMoodStats(List<MoodRecord> records) {
final stats = <String, int>{};
for (var record in records) {
stats[record.mood] = (stats[record.mood] ?? 0) + 1;
}
return stats;
}
统计每种心情出现的次数,然后用饼图展示。这样用户可以看出自己最近的情绪状态,是开心的时候多,还是难过的时候多。
心情趋势分析
Widget _buildMoodTrend(List<MoodRecord> records) {
// 按日期分组
final groupedRecords = <String, List<MoodRecord>>{};
for (var record in records) {
final dateKey = DateFormat('yyyy-MM-dd').format(record.date);
groupedRecords[dateKey] = groupedRecords[dateKey] ?? [];
groupedRecords[dateKey]!.add(record);
}
// 计算每天的平均心情分数
final trendData = <FlSpot>[];
int index = 0;
groupedRecords.forEach((date, dayRecords) {
double avgScore = 0;
for (var record in dayRecords) {
avgScore += getMoodScore(record.mood);
}
avgScore /= dayRecords.length;
trendData.add(FlSpot(index.toDouble(), avgScore));
index++;
});
return LineChart(
LineChartData(
lineBarsData: [
LineChartBarData(
spots: trendData,
isCurved: true,
color: Colors.blue,
),
],
),
);
}
double getMoodScore(String mood) {
switch (mood) {
case '很开心': return 5;
case '开心': return 4;
case '一般': return 3;
case '难过': return 2;
case '很难过': return 1;
default: return 3;
}
}
把心情转换成分数,然后画成折线图。这样可以看出心情的变化趋势,是越来越好,还是越来越差。
提醒功能
可以设置每天固定时间提醒用户记录心情:
Future<void> scheduleMoodReminder(TimeOfDay time) async {
// 使用flutter_local_notifications包
final notificationTime = Time(time.hour, time.minute, 0);
await flutterLocalNotificationsPlugin.zonedSchedule(
0,
'记录今天的心情',
'花一分钟记录下今天的心情吧',
_nextInstanceOfTime(notificationTime),
const NotificationDetails(
android: AndroidNotificationDetails(
'mood_reminder',
'心情提醒',
channelDescription: '每天提醒记录心情',
),
),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.time,
);
}
每天固定时间发送通知,提醒用户记录心情。这个功能对于养成记录习惯很有帮助。
实际使用体验
我自己用了一段时间心情日记功能,感觉真的很有用。每天睡前花一分钟记录下今天的心情,慢慢就成了习惯。
有时候回头看看以前的记录,会发现很多有趣的事情。比如发现自己每周五心情都特别好,因为周末要来了;每次加班后心情都不太好,说明工作压力确实大。
通过心情统计,我发现自己开心的时候其实挺多的,只是难过的时候印象更深刻。这让我对生活的态度更积极了。
可以改进的地方
如果要做得更完善,可以考虑以下几点。
更多心情选项
可以加入更细致的心情分类,比如焦虑、兴奋、平静等。但要注意不要太多,太多了用户反而不知道选哪个。
心情标签
除了选择心情,还可以加标签,比如"工作"、“家庭”、"朋友"等。这样可以分析不同领域对心情的影响。
照片支持
允许用户在心情记录里添加照片,这样记录会更丰富。一张照片往往能唤起很多回忆。
心情分享
可以把心情记录分享给朋友或家人,让他们了解你的状态。当然,这个功能要做好隐私保护。
心理健康建议
根据用户的心情记录,给出一些心理健康建议。比如连续多天心情不好,就提醒用户注意休息,或者寻求专业帮助。
小结
心情日记功能看起来简单,但对用户的价值很大。通过记录心情,用户可以更好地了解自己,管理情绪,保持心理健康。
在实现过程中,我特别注重温暖的设计。柔和的颜色、亲切的文字、生动的表情,这些细节都是为了让用户感到舒适和放松。记录心情应该是一件轻松愉快的事情,而不是负担。
从我自己的使用体验来看,这个功能确实帮助我更好地认识了自己。希望这篇文章能给你带来一些启发。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)