在这里插入图片描述

说起记录心情,我以前觉得这是个很矫情的事情。但后来发现,定期记录自己的情绪变化,对心理健康真的很有帮助。有时候回头看看以前的心情记录,会发现很多当时觉得天大的事情,现在看来也就那样。所以在做这个生活助手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是纵向间距。

选中状态的视觉反馈

选中的心情卡片有两个视觉变化:

  1. 背景色变成对应颜色的半透明版本(withOpacity(0.2)
  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类来表示心情记录,包含心情、内容和日期三个字段。toJsonfromJson方法用于序列化和反序列化。

保存记录的逻辑

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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐