Flutter for OpenHarmony:言隅 - 用 Flutter 打造诗意灵感收集器的艺术实践
Flutter for OpenHarmony:言隅 - 用 Flutter 打造诗意灵感收集器的艺术实践
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
发布时间:2026年2月9日
技术栈:Flutter 3.22+、Dart 3.4+、ListView、TextField、状态管理、时间格式化
项目类型:创意工具 / 灵感捕捉 / 教育级 CRUD 应用
适用读者:Flutter 开发者、写作者、设计师、对“微创作”有需求的用户
引言:在信息洪流中,为灵光一现留一方净土
我们都有过这样的瞬间:地铁上瞥见一句诗、会议间隙闪过一个金句、深夜突然悟出一段哲思——但转瞬即逝,未及记录。而传统笔记应用太重,社交平台又太喧嚣。
《言隅》(PhraseNook)正是为此而生:一个极简、专注、无干扰的灵感短语收集器。它不分类、不标签、不联网,只提供最纯粹的功能——输入、保存、回看。所有数据仅在当前页面生命周期内存在,刷新即清空,却足以守护那些稍纵即逝的“啊哈时刻”。
本文将深入剖析其五大核心维度:
- 可展开文本的智能截断与交互设计
- 时间感知的格式化策略:从“刚刚”到“历史”
- 搜索驱动的动态过滤机制
- 双模操作:点击展开 + 长按删除
- 克制式产品哲学:为何不持久化?
并探讨如何在零外部依赖的前提下,打造一个兼具诗意与工程严谨的微型创作空间。
一、内容呈现:可展开文本的优雅处理
1.1 智能截断逻辑
final displayText = isExpanded
? phrase.content
: phrase.content.length > 80
? '${phrase.content.substring(0, 80)}…'
: phrase.content;
设计考量:
- 80字符阈值:约等于中文40字,在手机屏幕上显示3–4行,避免卡片过高
- 省略号暗示:明确告知内容被截断,激发用户点击欲望
- 无缝切换:展开/收起时文本平滑过渡,无闪烁
1.2 展开指示器
if (phrase.content.length > 80)
Text(isExpanded ? '▲ 收起' : '▼ 展开', style: TextStyle(color: primary))

- 语义化图标:▲/▼ 符合用户心智模型
- 主色强调:使用主题主色,引导交互
- 条件渲染:仅当内容超长时显示,避免视觉噪音
✍️ 写作心理学:
灵感短语的价值在于“完整阅读”。截断不是限制,而是邀请。
二、时间感知:上下文敏感的时间格式化
2.1 智能时间显示
String _formatTime(DateTime time) {
final now = DateTime.now();
if (time.year == now.year && time.month == now.month && time.day == now.day) {
return '${time.hour}:${time.minute}';
}
return '${time.month}/${time.day} ${time.hour}:${time.minute}';
}

时间语义分层:
| 场景 | 显示格式 | 用户认知 |
|---|---|---|
| 今天创建 | 14:30 |
“刚刚写的” |
| 非今天 | 2/5 14:30 |
“前几天的灵感” |
- 省去年份:因数据仅会话内存在,年份冗余
- 无“昨天/前天”:避免复杂逻辑,保持简洁
2.3 时间位置与样式
Text(_formatTime(...), style: TextStyle(color: grey, fontSize: 12))
- 底部弱化:灰色小字号,不抢夺内容注意力
- 垂直间距:
SizedBox(height: 8)确保与正文分离
⏳ 时间作为上下文:
不是冰冷的时间戳,而是灵感的“温度计”。
三、搜索系统:轻量级动态过滤
3.1 实时搜索实现
TextField(
controller: _searchController,
onChanged: (_) => setState(() {}),
)
- 无防抖:因数据量小(会话内),实时响应更流畅
- 大小写不敏感:
toLowerCase()确保搜索友好
3.2 过滤逻辑
List<Phrase> get _filteredPhrases {
final query = _searchController.text.toLowerCase().trim();
if (query.isEmpty) return _allPhrases;
return _allPhrases.where((p) => p.content.toLowerCase().contains(query)).toList();
}

- 计算属性:确保始终返回最新结果
- 子串匹配:支持关键词任意位置匹配
🔍 搜索即过滤:
在微型应用中,搜索与筛选无需区分。
四、交互设计:双模操作,直觉优先
4.1 点击 → 展开/收起
onTap: () => _toggleExpand(phrase.id)
- 主交互路径:点击是最高频操作,用于查看完整内容
- 状态记忆:
_expandedIdsSet 记录每个短语的展开状态
4.2 长按 → 删除
onLongPress: () => _showDeleteDialog(phrase)
- 危险操作防护:AlertDialog 二次确认
- 内容预览:删除对话框中显示短语片段,避免误删
- 视觉警示:红色“删除”按钮强化操作后果
✋ 手势语义映射:
轻触探索,长按清理——符合物理世界直觉。
五、工程亮点与最佳实践
5.1 轻量级状态管理
- 三个核心状态:
_allPhrases:所有短语_expandedIds:展开状态集合_searchController:搜索文本
- 局部更新:
setState仅重建必要部分 - 无外部依赖:纯 Flutter 实现,教学友好
5.2 最新优先排序
_allPhrases.insert(0, newPhrase); // 最新在最前
- 符合创作流:用户最关心刚写下的内容
- 无需排序字段:插入位置即隐含时间序
5.3 空状态引导
Icon(Icons.edit_note_outlined, size: 64, color: Colors.grey),
Text('暂无短语'),
Text('点击“+”添加你的第一句灵感', style: TextStyle(fontSize: 12)),
- 符号联想:编辑笔记图标建立功能认知
- 行动号召:明确下一步操作
- 情感化文案:“灵感”比“内容”更具吸引力
六、克制式设计:为何不持久化?
6.1 产品定位清晰
- 临时性容器:捕捉“此刻”的灵感,而非永久归档
- 降低心理负担:用户无需思考“要不要删旧内容”
- 隐私友好:敏感文字(如日记片段)不应留存设备
6.2 技术权衡
- Web 平台限制:localStorage 有大小限制且需用户授权
- MVP 原则:先验证核心价值(快速记录/回看),再考虑增强
- 教学纯净性:避免引入
shared_preferences等复杂概念
🧘 禅意设计:
有些东西,本就不该被保存。它的价值在于被写下那一刻。
七、进阶演进方向
7.1 功能增强
- 导出分享:
- 添加“分享全部”按钮,生成文本或图片
- 随机展示:
- “抽一句”功能,随机显示一条短语,用于每日灵感
- 字数统计:
- 在输入框下方显示字数,辅助创作
7.2 技术升级
- 本地持久化(可选):
// 使用 hive 或 shared_preferences box.put('phrases', _allPhrases.map((p) => jsonEncode(p)).toList()); - 动画过渡:
- 添加
AnimatedSize实现展开/收起动画
- 添加
- PWA 支持(Web):
- 支持安装到桌面,提升使用频率
7.3 设计深化
- 字体选择:
- 允许用户切换衬线/无衬线字体,适配阅读偏好
- 背景纹理:
- 添加纸张、黑板等背景,增强创作氛围
- 语音输入:
- 集成语音识别,支持口述灵感(需权限)
结语:在数字时代,守护文字的纯粹
《言隅》是一次对“效率至上”设计的温柔反抗。它不追求功能全面,而是专注于一个微小却深刻的场景:让灵光一现的文字,有处安放。
在信息过载的时代,《言隅》证明了:最好的工具,往往看起来“什么都没做”。它没有云同步,没有历史版本,甚至没有保存按钮——但它精准地服务于创作者最原始的需求:写下,然后看见。
对于开发者而言,这不仅是一个短语收集器,更是一面镜子——照见我们是否真正理解用户,是否敢于对“加功能”的惯性说不。
“Writing is an act of faith, not a trick of grammar.”
—— E.B. White
愿你的下一个应用,也能在喧嚣世界中,为文字留一片宁静的角落。
GitHub Gist 链接:phrase_nook_app.dart
适用场景:灵感捕捉、Flutter ListView 教学、可展开文本实践、双模交互范例
📝 Happy Coding!
让每一行代码,都成为文字的守护者。
更多推荐



所有评论(0)