flutter_for_openharmony家庭相册app实战+回忆列表实现
本文介绍了家庭回忆列表功能的实现方案。文章首先阐述了卡片式布局的设计思路,然后详细展示了Flutter代码实现过程: 页面结构采用Consumer监听数据变化,使用ListView.builder高效渲染回忆卡片,并添加悬浮按钮用于新增回忆。 空状态设计包含图标提示、文字说明和引导按钮,提升用户体验。 回忆卡片采用InkWell实现点击效果,分为三部分: 头部显示标题和日期标签 中部展示回忆描述

回忆列表是记录家庭美好时光的地方,每个回忆都包含标题、日期、描述和相关照片。今天我们来实现这个功能,让用户可以方便地浏览和管理所有回忆。
设计思路
回忆列表采用卡片式布局,每个卡片展示回忆的关键信息。列表按时间倒序排列,最新的回忆显示在最前面。点击卡片可以查看回忆的详细内容。
创建页面结构
先搭建基本的页面框架:
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:provider/provider.dart';
import 'package:intl/intl.dart';
import '../providers/family_provider.dart';
import '../models/memory.dart';
import 'memory_detail_screen.dart';
import 'add_memory_screen.dart';
class MemoryListScreen extends StatelessWidget {
const MemoryListScreen({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('回忆列表'),
elevation: 0,
),
body: Consumer<FamilyProvider>(
builder: (context, provider, _) {
final memories = provider.memories;
if (memories.isEmpty) {
return _buildEmptyState(context);
}
return ListView.builder(
padding: EdgeInsets.all(16.w),
itemCount: memories.length,
itemBuilder: (context, index) {
final memory = memories[index];
return _buildMemoryCard(context, memory);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const AddMemoryScreen(),
),
);
},
backgroundColor: const Color(0xFFE91E63),
child: const Icon(Icons.add),
),
);
}
}
这里用Consumer监听FamilyProvider,当回忆数据变化时页面会自动更新。ListView.builder可以高效地渲染大量回忆卡片。右下角的悬浮按钮用来添加新回忆。
空状态设计
当还没有回忆时,显示一个友好的提示界面:
Widget _buildEmptyState(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.photo_library_outlined,
size: 80.sp,
color: Colors.grey[300],
),
SizedBox(height: 20.h),
Text(
'还没有回忆',
style: TextStyle(
fontSize: 18.sp,
color: Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 8.h),
Text(
'点击右下角按钮添加第一个回忆',
style: TextStyle(
fontSize: 14.sp,
color: Colors.grey[400],
),
),
SizedBox(height: 24.h),
ElevatedButton.icon(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const AddMemoryScreen(),
),
);
},
icon: const Icon(Icons.add),
label: const Text('添加回忆'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE91E63),
padding: EdgeInsets.symmetric(
horizontal: 24.w,
vertical: 12.h,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24.r),
),
),
),
],
),
);
}
空状态不仅有图标和文字提示,还加了一个按钮,用户可以直接点击开始添加回忆。这样的设计比较友好,不会让用户感到迷茫。
回忆卡片设计
每个回忆用一个精美的卡片来展示:
Widget _buildMemoryCard(BuildContext context, Memory memory) {
return Card(
margin: EdgeInsets.only(bottom: 16.h),
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => MemoryDetailScreen(memory: memory),
),
);
},
borderRadius: BorderRadius.circular(16.r),
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCardHeader(memory),
if (memory.description.isNotEmpty) ...[
SizedBox(height: 12.h),
_buildDescription(memory.description),
],
SizedBox(height: 12.h),
_buildCardFooter(memory),
],
),
),
),
);
}
卡片用InkWell包装,点击时有水波纹效果。内容分为三部分:头部显示标题和日期,中间显示描述,底部显示照片和家人数量。
卡片头部
头部包含标题和日期标签:
Widget _buildCardHeader(Memory memory) {
return Row(
children: [
Expanded(
child: Text(
memory.title,
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
SizedBox(width: 12.w),
Container(
padding: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 6.h,
),
decoration: BoxDecoration(
color: const Color(0xFFE91E63).withOpacity(0.1),
borderRadius: BorderRadius.circular(16.r),
),
child: Text(
DateFormat('MM-dd').format(memory.date),
style: TextStyle(
fontSize: 12.sp,
color: const Color(0xFFE91E63),
fontWeight: FontWeight.w600,
),
),
),
],
);
}
标题用粗体显示,最多两行,超出部分用省略号。日期用一个圆角标签展示,颜色和主题色保持一致。
描述文本
如果回忆有描述,就显示出来:
Widget _buildDescription(String description) {
return Text(
description,
style: TextStyle(
fontSize: 14.sp,
color: Colors.grey[700],
height: 1.5,
),
maxLines: 3,
overflow: TextOverflow.ellipsis,
);
}
描述文字用灰色,行高设为1.5让文字看起来不那么拥挤。最多显示三行,超出的部分用省略号表示。
卡片底部
底部显示照片数量和参与的家人数量:
Widget _buildCardFooter(Memory memory) {
return Row(
children: [
_buildInfoChip(
icon: Icons.photo_outlined,
label: '${memory.photoIds.length} 张照片',
color: const Color(0xFF2196F3),
),
SizedBox(width: 12.w),
_buildInfoChip(
icon: Icons.people_outline,
label: '${memory.memberIds.length} 位家人',
color: const Color(0xFF4CAF50),
),
const Spacer(),
Icon(
Icons.chevron_right,
color: Colors.grey[400],
size: 20.sp,
),
],
);
}
Widget _buildInfoChip({
required IconData icon,
required String label,
required Color color,
}) {
return Container(
padding: EdgeInsets.symmetric(
horizontal: 10.w,
vertical: 6.h,
),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12.r),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
icon,
size: 14.sp,
color: color,
),
SizedBox(width: 4.w),
Text(
label,
style: TextStyle(
fontSize: 12.sp,
color: color,
fontWeight: FontWeight.w500,
),
),
],
),
);
}
照片和家人数量用小标签展示,每个标签有图标和文字。不同的信息用不同的颜色,这样一眼就能看出来。右边的箭头提示用户可以点击查看详情。
添加下拉刷新
让用户可以下拉刷新回忆列表:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('回忆列表'),
elevation: 0,
),
body: Consumer<FamilyProvider>(
builder: (context, provider, _) {
final memories = provider.memories;
if (memories.isEmpty) {
return _buildEmptyState(context);
}
return RefreshIndicator(
onRefresh: () async {
await provider.refreshMemories();
},
color: const Color(0xFFE91E63),
child: ListView.builder(
padding: EdgeInsets.all(16.w),
itemCount: memories.length,
itemBuilder: (context, index) {
final memory = memories[index];
return _buildMemoryCard(context, memory);
},
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const AddMemoryScreen(),
),
);
},
backgroundColor: const Color(0xFFE91E63),
child: const Icon(Icons.add),
),
);
}
RefreshIndicator包装ListView,用户下拉时会触发刷新。刷新指示器的颜色设为主题色,保持视觉统一。
添加筛选功能
在AppBar上添加筛选按钮,让用户可以按条件筛选回忆:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('回忆列表'),
elevation: 0,
actions: [
IconButton(
icon: const Icon(Icons.filter_list),
onPressed: () => _showFilterOptions(context),
),
],
),
body: Consumer<FamilyProvider>(
builder: (context, provider, _) {
final memories = provider.memories;
if (memories.isEmpty) {
return _buildEmptyState(context);
}
return RefreshIndicator(
onRefresh: () async {
await provider.refreshMemories();
},
color: const Color(0xFFE91E63),
child: ListView.builder(
padding: EdgeInsets.all(16.w),
itemCount: memories.length,
itemBuilder: (context, index) {
final memory = memories[index];
return _buildMemoryCard(context, memory);
},
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const AddMemoryScreen(),
),
);
},
backgroundColor: const Color(0xFFE91E63),
child: const Icon(Icons.add),
),
);
}
右上角的筛选按钮点击后会弹出筛选选项,用户可以选择按时间、按家人等条件来筛选回忆。
筛选选项对话框
显示各种筛选条件:
void _showFilterOptions(BuildContext context) {
showModalBottomSheet(
context: context,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20.r),
),
),
builder: (sheetContext) => SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 8.h),
Container(
width: 40.w,
height: 4.h,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(2.r),
),
),
SizedBox(height: 16.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w),
child: Row(
children: [
Text(
'筛选条件',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
TextButton(
onPressed: () {
Navigator.pop(sheetContext);
context.read<FamilyProvider>().clearMemoryFilter();
},
child: const Text('清除'),
),
],
),
),
const Divider(),
ListTile(
leading: const Icon(Icons.calendar_today, color: Color(0xFF2196F3)),
title: const Text('按时间排序'),
trailing: const Icon(Icons.chevron_right),
onTap: () {
Navigator.pop(sheetContext);
_showSortOptions(context);
},
),
ListTile(
leading: const Icon(Icons.people, color: Color(0xFF4CAF50)),
title: const Text('按家人筛选'),
trailing: const Icon(Icons.chevron_right),
onTap: () {
Navigator.pop(sheetContext);
_showMemberFilter(context);
},
),
ListTile(
leading: const Icon(Icons.photo, color: Color(0xFFFF9800)),
title: const Text('只看有照片的'),
trailing: const Icon(Icons.chevron_right),
onTap: () {
Navigator.pop(sheetContext);
context.read<FamilyProvider>().filterMemoriesWithPhotos();
},
),
SizedBox(height: 16.h),
],
),
),
);
}
底部菜单提供了三个筛选选项:按时间排序、按家人筛选、只看有照片的回忆。每个选项都有对应的图标和颜色,让界面更加生动。
排序选项
让用户选择按时间升序还是降序:
void _showSortOptions(BuildContext context) {
showDialog(
context: context,
builder: (dialogContext) => AlertDialog(
title: const Text('排序方式'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<String>(
title: const Text('最新在前'),
value: 'desc',
groupValue: 'desc',
onChanged: (value) {
context.read<FamilyProvider>().sortMemories(descending: true);
Navigator.pop(dialogContext);
},
activeColor: const Color(0xFFE91E63),
),
RadioListTile<String>(
title: const Text('最早在前'),
value: 'asc',
groupValue: 'desc',
onChanged: (value) {
context.read<FamilyProvider>().sortMemories(descending: false);
Navigator.pop(dialogContext);
},
activeColor: const Color(0xFFE91E63),
),
],
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
),
);
}
用RadioListTile实现单选,用户选择后立即应用排序并关闭对话框。
家人筛选
让用户选择要查看哪位家人的回忆:
void _showMemberFilter(BuildContext context) {
final provider = context.read<FamilyProvider>();
final members = provider.familyMembers;
showDialog(
context: context,
builder: (dialogContext) => AlertDialog(
title: const Text('选择家人'),
content: SizedBox(
width: double.maxFinite,
child: ListView.builder(
shrinkWrap: true,
itemCount: members.length,
itemBuilder: (context, index) {
final member = members[index];
return ListTile(
leading: CircleAvatar(
backgroundColor: _getColorForMember(member.avatar),
child: Text(
member.name.substring(0, 1),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
title: Text(member.name),
onTap: () {
provider.filterMemoriesByMember(member.id);
Navigator.pop(dialogContext);
},
);
},
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
),
);
}
Color _getColorForMember(String avatar) {
final colors = [
const Color(0xFFE91E63),
const Color(0xFF9C27B0),
const Color(0xFF3F51B5),
const Color(0xFF2196F3),
const Color(0xFF009688),
const Color(0xFF4CAF50),
];
return colors[avatar.hashCode.abs() % colors.length];
}
对话框里显示所有家人的列表,每个家人有头像和名字。用户点击某个家人,就只显示包含这个家人的回忆。
总结
回忆列表页面通过精美的卡片设计展示所有回忆,每个卡片包含标题、日期、描述和统计信息。下拉刷新和筛选功能让用户可以更方便地管理和查找回忆。空状态设计友好,引导用户添加第一个回忆。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)