Flutter for OpenHarmony 看书管理记录App实战:书籍详情实现
本文介绍了如何使用Flutter实现一个书籍详情页,主要采用CustomScrollView和SliverAppBar实现滚动折叠效果。页面结构包括:1)可折叠的SliverAppBar头部区域,包含背景渐变和操作按钮;2)主体内容区分为书籍信息、阅读状态、内容简介、读书笔记等模块。重点展示了书籍信息卡片的实现细节,包括标题样式、作者/出版社信息展示、标签式元数据布局以及评分显示组件。通过Sliv
书籍详情页是用户查看单本书完整信息的地方,包括书籍基本信息、阅读进度、内容简介、读书笔记等。今天来实现这个页面,主要用到 CustomScrollView 和 SliverAppBar 来实现滚动时的头部效果。
做详情页的时候,我参考了豆瓣读书的设计。顶部是一个可折叠的头部区域,滚动时会收起来,只保留标题栏。这种效果用 SliverAppBar 很容易实现。
页面整体结构
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import '../../app/routes/app_routes.dart';
class BookDetailPage extends StatelessWidget {
const BookDetailPage({super.key});
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFFDF8F3),
body: CustomScrollView(
slivers: [
用 CustomScrollView 配合 Sliver 系列组件,可以实现复杂的滚动效果。slivers 数组里放各种 Sliver 组件。
SliverAppBar 配置
SliverAppBar(
expandedHeight: 200.h,
pinned: true,
backgroundColor: const Color(0xFF5B4636),
foregroundColor: Colors.white,
actions: [
IconButton(icon: const Icon(Icons.favorite_border), onPressed: () {}),
IconButton(icon: const Icon(Icons.edit), onPressed: () => Get.toNamed(AppRoutes.editBook)),
],
expandedHeight: 200.h 设置展开时的高度。pinned: true 让AppBar在滚动时固定在顶部,不会完全消失。
右上角放了收藏和编辑两个按钮,收藏按钮用空心爱心图标,点击后可以切换成实心。
FlexibleSpaceBar 背景
flexibleSpace: FlexibleSpaceBar(
background: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF5B4636), Color(0xFF8B7355)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter
)
),
child: Center(child: Icon(Icons.menu_book, size: 80.sp, color: Colors.white24)),
),
),
),
FlexibleSpaceBar 的 background 是展开时显示的内容。用渐变背景加一个半透明的书籍图标,实际项目中这里应该显示书籍封面。
渐变方向是从上到下,和 AppBar 的颜色衔接自然。
页面主体内容
SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildBookInfo(),
SizedBox(height: 20.h),
_buildReadingStatus(),
SizedBox(height: 20.h),
_buildDescription(),
SizedBox(height: 20.h),
_buildNotes(),
SizedBox(height: 20.h),
_buildActions(),
],
),
),
),
],
),
);
}
SliverToBoxAdapter 把普通的 Widget 转换成 Sliver,这样就能放在 CustomScrollView 里了。页面内容分成五个模块:书籍信息、阅读状态、内容简介、读书笔记、操作按钮。
书籍基本信息
Widget _buildBookInfo() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12.r)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('百年孤独', style: TextStyle(fontSize: 22.sp, fontWeight: FontWeight.bold, color: const Color(0xFF3D2914))),
SizedBox(height: 8.h),
Row(children: [
Icon(Icons.person_outline, size: 16.sp, color: Colors.grey[600]),
SizedBox(width: 4.w),
Text('加西亚·马尔克斯', style: TextStyle(color: Colors.grey[600], fontSize: 14.sp)),
]),
SizedBox(height: 4.h),
Row(children: [
Icon(Icons.business, size: 16.sp, color: Colors.grey[600]),
SizedBox(width: 4.w),
Text('南海出版公司', style: TextStyle(color: Colors.grey[600], fontSize: 14.sp)),
]),
书名用大号加粗字体,是整个卡片的视觉焦点。作者和出版社用图标加文字的形式,图标让信息更直观。
信息标签
SizedBox(height: 12.h),
Row(
children: [
_buildInfoChip(Icons.book, '380页'),
SizedBox(width: 12.w),
_buildInfoChip(Icons.category, '文学'),
SizedBox(width: 12.w),
_buildInfoChip(Icons.calendar_today, '2017'),
],
),
用小标签展示页数、分类、出版年份等信息。标签用圆角胶囊形状,看起来更精致。
标签组件
Widget _buildInfoChip(IconData icon, String text) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 6.h),
decoration: BoxDecoration(color: const Color(0xFF5B4636).withOpacity(0.1), borderRadius: BorderRadius.circular(20.r)),
child: Row(mainAxisSize: MainAxisSize.min, children: [
Icon(icon, size: 14.sp, color: const Color(0xFF5B4636)),
SizedBox(width: 4.w),
Text(text, style: TextStyle(fontSize: 12.sp, color: const Color(0xFF5B4636))),
]),
);
}
标签组件抽成独立方法,方便复用。mainAxisSize: MainAxisSize.min 让 Row 只占用必要的宽度,不会撑满整行。
评分显示
SizedBox(height: 12.h),
Row(
children: [
...List.generate(5, (i) => Icon(i < 4 ? Icons.star : Icons.star_half, color: Colors.amber, size: 20.sp)),
SizedBox(width: 8.w),
Text('4.5', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
],
),
],
),
);
}
评分用五颗星显示,4.5分就是4颗实心星加1颗半星。List.generate 根据评分动态生成星星图标。
阅读进度卡片
Widget _buildReadingStatus() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12.r)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('阅读进度', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold, color: const Color(0xFF3D2914))),
SizedBox(height: 12.h),
Row(
children: [
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.circular(6.r),
child: LinearProgressIndicator(value: 0.75, backgroundColor: Colors.grey[200], valueColor: const AlwaysStoppedAnimation(Color(0xFF5B4636)), minHeight: 10.h),
),
),
SizedBox(width: 12.w),
Text('75%', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold, color: const Color(0xFF5B4636))),
],
),
进度条比首页的粗一些(10.h),因为这里是详情页,有更多空间展示。右边显示百分比数字。
进度详细信息
SizedBox(height: 12.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('已读 285 页 / 共 380 页', style: TextStyle(color: Colors.grey[600], fontSize: 13.sp)),
Text('开始于 2024-01-01', style: TextStyle(color: Colors.grey[600], fontSize: 13.sp)),
],
),
],
),
);
}
下面显示具体的页数和开始阅读日期,让用户对阅读进度有更清晰的了解。
内容简介
Widget _buildDescription() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12.r)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('内容简介', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold, color: const Color(0xFF3D2914))),
SizedBox(height: 12.h),
Text(
'《百年孤独》是魔幻现实主义文学的代表作,描写了布恩迪亚家族七代人的传奇故事,以及加勒比海沿岸小镇马孔多的百年兴衰,反映了拉丁美洲一个世纪以来风云变幻的历史。',
style: TextStyle(color: Colors.grey[700], fontSize: 14.sp, height: 1.6)
),
],
),
);
}
内容简介用 height: 1.6 增加行高,让文字更易读。简介文字不要太长,控制在两三行左右。
读书笔记入口
Widget _buildNotes() {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(12.r)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('读书笔记', style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold, color: const Color(0xFF3D2914))),
TextButton(onPressed: () => Get.toNamed(AppRoutes.noteList), child: const Text('查看全部')),
],
),
SizedBox(height: 8.h),
Text('共 5 条笔记', style: TextStyle(color: Colors.grey[600], fontSize: 13.sp)),
],
),
);
}
读书笔记模块显示笔记数量,点击"查看全部"跳转到笔记列表。这里只是一个入口,不展示具体内容。
操作按钮
Widget _buildActions() {
return Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: () => Get.toNamed(AppRoutes.addReadingRecord),
icon: const Icon(Icons.play_arrow),
label: const Text('记录阅读'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF5B4636),
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 14.h)
),
),
),
SizedBox(width: 12.w),
Expanded(
child: OutlinedButton.icon(
onPressed: () => Get.toNamed(AppRoutes.addNote),
icon: const Icon(Icons.edit_note),
label: const Text('写笔记'),
style: OutlinedButton.styleFrom(
foregroundColor: const Color(0xFF5B4636),
side: const BorderSide(color: Color(0xFF5B4636)),
padding: EdgeInsets.symmetric(vertical: 14.h)
),
),
),
],
);
}
}
底部两个操作按钮:记录阅读和写笔记。主按钮用实心样式,次按钮用描边样式,形成主次区分。
Expanded 让两个按钮等宽,padding: EdgeInsets.symmetric(vertical: 14.h) 增加按钮高度,更容易点击。
SliverAppBar 的优势
用 SliverAppBar 而不是普通 AppBar 的好处:
滚动时头部会自动折叠,节省屏幕空间。pinned: true 保证标题栏始终可见,用户随时能返回。
展开状态下可以显示更多信息,比如封面图片、背景渐变等。折叠后只保留必要的标题和按钮。
滚动效果流畅自然,是 Material Design 推荐的详情页模式。
小结
书籍详情页用 CustomScrollView + SliverAppBar 实现可折叠头部效果。页面内容分成多个卡片模块,每个模块职责单一。操作按钮放在底部,方便用户快速操作。
下一篇会讲添加书籍页面的实现,涉及到表单输入和状态选择,敬请期待。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐




所有评论(0)