Flutter for OpenHarmony 美食烹饪助手 App 实战:浏览历史记录实现
摘要: 本文介绍了如何实现一个浏览历史记录功能,帮助用户回顾之前查看的菜谱。采用列表布局展示历史记录,每个列表项包含菜谱图片、名称和相对浏览时间。通过ListView.builder优化性能,支持滑动删除单条记录和清空全部记录功能。文章详细讲解了UI设计、交互实现(点击跳转、滑动删除)以及相对时间计算方法,确保用户能直观查看和管理浏览历史。整个功能采用无状态组件实现,兼顾简洁性和实用性。

在浏览菜谱时,我们经常会遇到这样的情况:看到一道菜觉得不错,但没有立即收藏,过几天想起来却找不到了。今天我们要实现浏览历史记录功能,让用户能够回顾之前看过的所有菜谱。
浏览历史的设计思路
浏览历史要解决的核心问题是:如何让用户快速找到之前看过的菜谱?我选择了列表布局,因为历史记录通常按时间排序,列表最适合展示时间序列数据。
每个历史记录显示菜谱图片、名称和浏览时间。浏览时间很重要,它能帮助用户回忆是什么时候看的。比如"2小时前"、"昨天"这样的相对时间,比具体的日期时间更直观。
列表项可以点击进入详情页,也可以滑动删除。滑动删除是移动应用的通用交互,用户很容易理解。这种设计在邮件、消息等应用中很常见。
创建无状态组件
浏览历史页面的内容相对固定,使用 StatelessWidget 就够了。
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class BrowseHistoryPage extends StatelessWidget {
const BrowseHistoryPage({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('浏览历史'),
actions: [
TextButton(
onPressed: () {},
child: const Text('清空', style: TextStyle(color: Colors.white)),
),
],
),
AppBar 右侧放了一个"清空"按钮,用于清空所有历史记录。按钮使用白色文字,和 AppBar 的背景色对比明显。
清空是一个比较重要的操作,所以放在 AppBar 上,让用户容易找到。点击后应该弹出确认对话框,避免误操作。
body: ListView.builder(
padding: EdgeInsets.all(16.w),
itemCount: 30,
itemBuilder: (context, index) => _buildItem(index),
),
);
}
body 使用 ListView.builder 来展示历史记录。padding 设置为 16.w,让列表内容不要紧贴屏幕边缘。
itemCount 现在是固定的 30,实际开发中应该根据历史记录数量动态设置。历史记录可能很多,所以使用 ListView.builder 而不是 ListView,这样只会渲染可见区域的内容。
实现列表项
每个列表项包含图片、名称和浏览时间。
Widget _buildItem(int index) {
return Container(
margin: EdgeInsets.only(bottom: 12.h),
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
),
列表项使用卡片样式,白色背景和圆角。margin 设置为 bottom: 12.h,让卡片之间有间距。padding 设置为 12.w,让内容不要紧贴边缘。
这里没有设置阴影,因为背景色已经是浅灰色了,白色卡片本身就有足够的对比度。
child: Row(
children: [
Container(
width: 60.w,
height: 60.h,
decoration: BoxDecoration(
color: Colors.orange.shade100,
borderRadius: BorderRadius.circular(8.r),
),
child: Icon(Icons.restaurant, size: 30.sp, color: Colors.orange),
),
左侧是菜谱图片的占位符。实际开发中应该显示真实的图片,这里用图标代替。容器大小 60x60,比其他页面的图片稍小一些,因为历史记录不需要太大的图片。
背景色是橙色的浅色版本,圆角 8.r。图标大小 30.sp,颜色是橙色,和背景色搭配。
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('历史菜谱 ${index + 1}', style: TextStyle(fontSize: 14.sp, fontWeight: FontWeight.bold)),
SizedBox(height: 4.h),
Text('浏览于 2小时前', style: TextStyle(fontSize: 11.sp, color: Colors.grey)),
],
),
),
],
),
);
}
}
右侧是菜谱信息。使用 Expanded 让这部分占据剩余空间。菜谱名称使用粗体,字号 14.sp。
浏览时间使用灰色,字号 11.sp,表示这是次要信息。时间使用相对时间格式,比如"2小时前"、"昨天"等,这比具体的日期时间更直观。
实现相对时间
相对时间的计算需要一个辅助函数:
String getRelativeTime(DateTime dateTime) {
final now = DateTime.now();
final difference = now.difference(dateTime);
if (difference.inMinutes < 60) {
return '${difference.inMinutes}分钟前';
} else if (difference.inHours < 24) {
return '${difference.inHours}小时前';
} else if (difference.inDays == 1) {
return '昨天';
} else if (difference.inDays < 7) {
return '${difference.inDays}天前';
} else {
return '${dateTime.month}月${dateTime.day}日';
}
}
这个函数根据时间差返回不同的格式。1小时内显示分钟,24小时内显示小时,1天显示"昨天",7天内显示天数,超过7天显示具体日期。
这种相对时间格式在社交应用中很常见,用户很容易理解。它比具体的日期时间更有亲和力。
添加点击交互
点击列表项应该跳转到菜谱详情页:
Widget _buildItem(int index) {
return GestureDetector(
onTap: () {
Get.to(() => RecipeDetailPage(recipeId: 'recipe_$index'));
},
child: Container(
// ...
),
);
}
使用 GestureDetector 包裹整个卡片,点击时跳转到详情页。传递菜谱 ID 作为参数,详情页会根据这个 ID 加载对应的菜谱数据。
跳转到详情页后,应该再次记录浏览历史。这样历史记录会更新,最近浏览的菜谱会排在前面。
实现滑动删除
用户可能想删除某些历史记录。可以使用 Dismissible 组件实现滑动删除:
Widget _buildItem(int index) {
return Dismissible(
key: Key('history_$index'),
direction: DismissDirection.endToStart,
background: Container(
alignment: Alignment.centerRight,
padding: EdgeInsets.only(right: 20.w),
color: Colors.red,
child: Icon(Icons.delete, color: Colors.white),
),
onDismissed: (direction) {
// 删除历史记录
Get.snackbar('成功', '已删除历史记录');
},
child: Container(
// ...
),
);
}
Dismissible 组件可以让子组件支持滑动删除。key 是必需的,用于标识每个列表项。
direction 设置为 endToStart,表示只能从右向左滑动。这是 iOS 的标准交互,Android 用户也很熟悉。
background 是滑动时显示的背景。我们使用红色背景和删除图标,表示这是删除操作。图标靠右对齐,跟随手指滑动。
onDismissed 回调在滑动完成后触发。我们在这里执行删除操作,并显示提示。
实现清空功能
AppBar 上的"清空"按钮用于清空所有历史记录:
TextButton(
onPressed: () {
_confirmClearAll(context);
},
child: const Text('清空', style: TextStyle(color: Colors.white)),
)
点击后弹出确认对话框,避免误操作:
void _confirmClearAll(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('清空历史'),
content: Text('确定要清空所有浏览历史吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('取消'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
// 执行清空操作
Get.snackbar('成功', '已清空浏览历史');
},
child: Text('清空', style: TextStyle(color: Colors.red)),
),
],
);
},
);
}
确认对话框和删除确认类似。"清空"按钮使用红色,表示这是危险操作。
添加空状态
如果用户还没有浏览任何菜谱,需要显示空状态:
if (history.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.history, size: 64.sp, color: Colors.grey),
SizedBox(height: 16.h),
Text('还没有浏览记录', style: TextStyle(color: Colors.grey)),
SizedBox(height: 8.h),
ElevatedButton(
onPressed: () => Get.back(),
child: Text('去浏览菜谱'),
),
],
),
);
}
空状态使用历史图标,表示这是历史记录页面。文字说明当前状态,按钮引导用户去浏览菜谱。
添加分组功能
历史记录可以按日期分组,比如"今天"、“昨天”、"本周"等:
ListView.builder(
itemCount: groups.length,
itemBuilder: (context, index) {
final group = groups[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.w),
child: Text(
group.title,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: Colors.grey,
),
),
),
...group.items.map((item) => _buildItem(item)),
],
);
},
)
每个分组包含一个标题和多个列表项。标题使用灰色粗体,和列表项区分开。
这种分组方式能让用户更容易找到想要的历史记录。比如要找今天看过的菜谱,直接看"今天"这一组就行了。
添加搜索功能
如果历史记录很多,可以添加搜索功能:
AppBar(
title: TextField(
decoration: InputDecoration(
hintText: '搜索历史记录...',
border: InputBorder.none,
prefixIcon: Icon(Icons.search),
),
onChanged: (value) {
// 过滤历史记录
},
),
)
搜索框放在 AppBar 的 title 位置,用户输入关键词时实时过滤列表。这需要把 StatelessWidget 改成 StatefulWidget,用一个变量来存储过滤后的列表。
优化性能
历史记录可能很多,需要考虑性能优化。可以使用分页加载,每次只加载一部分数据:
ListView.builder(
controller: _scrollController,
itemCount: displayedItems.length + 1,
itemBuilder: (context, index) {
if (index == displayedItems.length) {
// 加载更多
return Center(child: CircularProgressIndicator());
}
return _buildItem(displayedItems[index]);
},
)
监听滚动位置,当滚动到底部时加载更多数据。这样可以避免一次性加载所有数据,提升性能。
总结
浏览历史记录功能使用列表布局,每个记录显示图片、名称和浏览时间。用户可以点击查看详情,也可以滑动删除或清空所有记录。
通过合理的交互设计,我们让历史记录功能既实用又方便。用户可以轻松回顾之前看过的菜谱,不用担心找不到喜欢的菜。
下一篇文章我们将实现菜谱搜索功能,让用户能够快速找到想要的菜谱。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)