Flutter for OpenHarmony生活助手App实战:日记本功能实现
摘要: 本文介绍了一款简洁实用的日记应用设计,强调记录生活的意义在于真实表达自我。应用采用卡片式布局,每条日记包含日期、心情emoji、标题和内容预览,支持时间轴浏览和日历跳转。设计注重隐私保护与使用体验,提供8种常用表情快速标记情绪,编辑页面简洁直观,内容预览限制3行并优化排版。整体设计以简单纯粹为核心,满足用户私密记录需求。
写日记这个习惯,我自己坚持了好几年。从最开始的纸质日记本,到后来的电子日记,再到现在自己开发的这个日记功能,记录生活的方式一直在变,但记录的意义从未改变。
为什么要做日记功能
你可能会想,现在社交媒体这么发达,发个朋友圈、微博不就行了吗?但说实话,真正的日记是写给自己看的,不需要考虑别人的眼光,可以完全真实地记录自己的想法和感受。
我在设计这个日记功能的时候,有几个核心想法:
- 简单纯粹:不要太多花里胡哨的功能,专注于记录本身
- 情绪标记:用emoji表情快速标记当天的心情
- 时间轴展示:按时间顺序展示,方便回顾
- 隐私保护:日记是很私密的东西,要做好加密保护
页面布局设计
日记本的页面我采用了列表布局,每条日记就像一张卡片,展示关键信息。先看看基本结构:
class DiaryPage extends StatelessWidget {
const DiaryPage({super.key});
Widget build(BuildContext context) {
final diaries = [
{'date': DateTime.now(), 'mood': '😊',
'title': '美好的一天', 'content': '今天天气很好,完成了很多工作...'},
{'date': DateTime.now().subtract(const Duration(days: 1)),
'mood': '😐', 'title': '平凡的一天',
'content': '普通的一天,没什么特别的...'},
];
每条日记包含日期、心情、标题、内容四个核心字段。心情用emoji表情来表示,这个设计我觉得特别好,一个表情就能传达很多信息。
AppBar设计
return Scaffold(
appBar: AppBar(
title: const Text('日记本'),
actions: [
IconButton(
icon: const Icon(Icons.calendar_today),
onPressed: () {},
),
],
),
AppBar右边放了一个日历图标,点击可以按日期查看日记。这个功能特别实用,想看某一天的日记,直接选日期就行,不用一条条翻。
日记卡片设计
每条日记的卡片设计是这样的:
body: ListView.builder(
padding: EdgeInsets.all(16.w),
itemCount: diaries.length,
itemBuilder: (context, index) {
final diary = diaries[index];
return Container(
margin: EdgeInsets.only(bottom: 16.h),
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
卡片用了白色背景,加了一点阴影效果,看起来有层次感。圆角半径12是我试了很多次才确定的,太小了显得生硬,太大了又不够稳重。
心情和标题展示
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
diary['mood'] as String,
style: TextStyle(fontSize: 32.sp),
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
diary['title'] as String,
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
),
SizedBox(height: 4.h),
Text(
DateFormat('yyyy年MM月dd日').format(diary['date'] as DateTime),
style: TextStyle(fontSize: 12.sp, color: Colors.grey),
),
],
),
),
],
),
这里把心情emoji放在最显眼的位置,字号32,一眼就能看到。标题和日期放在右边,形成一个清晰的信息层级。日期格式用的是中文格式,更符合阅读习惯。
内容预览
日记内容只显示前几行,点击后才展开全文:
SizedBox(height: 12.h),
Text(
diary['content'] as String,
style: TextStyle(fontSize: 14.sp, height: 1.5),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
maxLines: 3限制最多显示3行,overflow: TextOverflow.ellipsis超出部分用省略号。height: 1.5是行高,让文字看起来不那么拥挤,阅读体验更好。
添加日记功能
右下角的浮动按钮用来添加新日记:
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.edit),
),
这里用的是编辑图标而不是加号,因为写日记更像是在编辑文字。点击后应该跳转到编辑页面。
编辑页面设计
编辑页面需要包含心情选择、标题输入、内容输入三个部分:
class DiaryEditPage extends StatefulWidget {
const DiaryEditPage({super.key});
State<DiaryEditPage> createState() => _DiaryEditPageState();
}
class _DiaryEditPageState extends State<DiaryEditPage> {
String selectedMood = '😊';
final TextEditingController titleController = TextEditingController();
final TextEditingController contentController = TextEditingController();
final moods = ['😊', '😄', '😐', '😢', '😡', '😴', '🤔', '😎'];
心情选择器提供了8种常用表情,基本覆盖了日常的各种情绪。不要提供太多选择,太多了反而让人纠结。
心情选择器
Widget buildMoodSelector() {
return Container(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('今天的心情', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
SizedBox(height: 12.h),
Wrap(
spacing: 12.w,
children: moods.map((mood) {
final isSelected = mood == selectedMood;
return GestureDetector(
onTap: () => setState(() => selectedMood = mood),
child: Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: isSelected ? Colors.blue.withOpacity(0.1) : null,
borderRadius: BorderRadius.circular(8.r),
border: isSelected ? Border.all(color: Colors.blue, width: 2) : null,
),
child: Text(mood, style: TextStyle(fontSize: 32.sp)),
),
);
}).toList(),
),
],
),
);
}
选中的心情会有蓝色边框和背景色,视觉反馈很重要,让用户知道自己选了什么。
数据存储方案
日记数据需要持久化存储,而且要考虑隐私保护:
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import 'package:crypto/crypto.dart';
class DiaryStorage {
static const String _key = 'diary_data';
static Future<void> saveDiary(Map<String, dynamic> diary) async {
final prefs = await SharedPreferences.getInstance();
final diaries = await loadDiaries();
diaries.add(diary);
// 简单加密
final jsonString = jsonEncode(diaries);
final encrypted = _encrypt(jsonString);
await prefs.setString(_key, encrypted);
}
static String _encrypt(String text) {
// 这里应该用更安全的加密方式
return base64Encode(utf8.encode(text));
}
static String _decrypt(String encrypted) {
return utf8.decode(base64Decode(encrypted));
}
}
这里用了简单的Base64编码,实际项目中应该用更安全的加密算法,比如AES。日记是很私密的内容,加密保护必不可少。
日历视图功能
按日期查看日记是个很实用的功能:
import 'package:table_calendar/table_calendar.dart';
class DiaryCalendarPage extends StatefulWidget {
const DiaryCalendarPage({super.key});
State<DiaryCalendarPage> createState() => _DiaryCalendarPageState();
}
class _DiaryCalendarPageState extends State<DiaryCalendarPage> {
DateTime _selectedDay = DateTime.now();
DateTime _focusedDay = DateTime.now();
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('日历')),
body: Column(
children: [
TableCalendar(
firstDay: DateTime.utc(2020, 1, 1),
lastDay: DateTime.utc(2030, 12, 31),
focusedDay: _focusedDay,
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
onDaySelected: (selectedDay, focusedDay) {
setState(() {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
});
// 加载该日期的日记
},
),
],
),
);
}
}
用table_calendar包可以快速实现日历功能。选中某一天后,下方显示该天的日记内容。有日记的日期可以用特殊标记,比如小圆点,这样一眼就能看出哪些天写了日记。
搜索和筛选
日记多了之后,搜索功能就很重要了:
List<Map<String, dynamic>> searchDiaries(String query, List<Map<String, dynamic>> diaries) {
if (query.isEmpty) return diaries;
return diaries.where((diary) {
final title = diary['title'] as String;
final content = diary['content'] as String;
return title.contains(query) || content.contains(query);
}).toList();
}
List<Map<String, dynamic>> filterByMood(String mood, List<Map<String, dynamic>> diaries) {
return diaries.where((diary) => diary['mood'] == mood).toList();
}
可以按关键词搜索,也可以按心情筛选。比如想看看自己开心的时候都写了什么,就筛选😊表情的日记。这种回顾功能特别有意思,能看到自己的情绪变化。
导出功能
有时候想把日记导出来备份,或者打印出来:
import 'package:share_plus/share_plus.dart';
Future<void> exportDiary(Map<String, dynamic> diary) async {
final text = '''
${diary['title']}
${DateFormat('yyyy年MM月dd日').format(diary['date'] as DateTime)}
心情:${diary['mood']}
${diary['content']}
''';
await Share.share(text);
}
用share_plus包可以调用系统分享功能,把日记分享到其他应用,或者保存为文件。导出功能让数据更安全,不用担心丢失。
统计功能
看看自己写了多少篇日记,哪种心情最多,也是很有意思的:
Map<String, int> getMoodStatistics(List<Map<String, dynamic>> diaries) {
final stats = <String, int>{};
for (final diary in diaries) {
final mood = diary['mood'] as String;
stats[mood] = (stats[mood] ?? 0) + 1;
}
return stats;
}
Widget buildStatistics(Map<String, int> stats) {
return Column(
children: stats.entries.map((entry) {
return ListTile(
leading: Text(entry.key, style: TextStyle(fontSize: 32.sp)),
title: Text('${entry.value} 篇'),
trailing: Text('${(entry.value / stats.values.reduce((a, b) => a + b) * 100).toStringAsFixed(1)}%'),
);
}).toList(),
);
}
统计每种心情出现的次数和占比,可以了解自己的情绪状态。如果发现负面情绪太多,可能需要调整一下生活状态。
实际使用体验
我自己用这个日记功能已经有一段时间了,感觉还是挺好的。特别是心情标记这个功能,让我能快速回顾某段时间的情绪状态。
有时候翻看以前的日记,会发现很多有趣的事情。比如某个困扰了很久的问题,现在看来根本不算什么;某个当时觉得很重要的事情,现在已经完全忘记了。时间真的能改变很多东西。
不过也发现了一些可以改进的地方:
- 图片支持:有时候想在日记里插入照片,纯文字不够直观
- 天气记录:自动记录当天的天气,回顾时更有代入感
- 地点标记:记录写日记的地点,旅行日记特别有用
- 标签系统:可以给日记打标签,比如#工作 #生活 #旅行等
隐私保护建议
日记是很私密的内容,隐私保护要做好:
1. 本地加密:所有日记数据都要加密存储,不能明文保存。
2. 密码保护:可以设置一个密码,打开日记本时需要输入密码。
3. 指纹解锁:支持指纹或面部识别,更方便也更安全。
4. 云端备份:如果要云端备份,一定要端到端加密,服务器也看不到内容。
5. 隐藏功能:可以设置某些日记为隐藏,需要额外密码才能查看。
总结
日记功能看起来简单,但要做好需要考虑很多细节。最重要的是要让用户愿意写,愿意坚持写。界面要简洁,操作要流畅,隐私要保护好。
我在开发这个功能的时候,一直在思考怎么让它更好用。后来发现,好的日记应用不是功能最多的,而是最能让人坚持使用的。
如果你也在开发类似的功能,建议多从用户角度思考,多试用,多改进。一个好用的日记功能,真的能帮助人们更好地记录和回顾生活。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)