代码优化,功能新增

This commit is contained in:
DelLevin-Home
2026-06-26 23:46:37 +08:00
parent f7ef50a677
commit 45137b96d6
59 changed files with 5041 additions and 4937 deletions

View File

@@ -31,11 +31,15 @@ class _BookDetailPageState extends State<BookDetailPage> {
final ValueNotifier<bool> _draggingCover = ValueNotifier(false);
final GlobalKey _coverImageKey = GlobalKey();
double _coverImageHeight = 0.0;
final ValueNotifier<bool> _showTitle = ValueNotifier(false);
ScrollController? _overlayScrollController;
@override
void dispose() {
_coverOffset.dispose();
_draggingCover.dispose();
_showTitle.dispose();
_overlayScrollController?.dispose();
super.dispose();
}
@@ -66,25 +70,20 @@ class _BookDetailPageState extends State<BookDetailPage> {
backgroundColor: colors.surface,
body: Stack(
children: [
// 图片从导航栏下方开始,固定在顶部
Column(
children: [
SizedBox(height: topSafe + 48),
SizedBox(
height: 320,
width: double.infinity,
child: _buildCoverSection(book),
),
],
),
// 可滚动内容区域从图片底部开始
Positioned(
top: topSafe + 48 + 320,
left: 0, right: 0, bottom: 0,
// 整体可滚动(封面 + 内容一起滑动)
Padding(
padding: EdgeInsets.only(top: topSafe + 48),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 封面图
SizedBox(
height: 320,
width: double.infinity,
child: _buildCoverSection(book),
),
// 详细信息
_buildBasicInfo(book),
Divider(height: 0.5, thickness: 0.5, color: colors.outline),
_buildAuthorsSection(book),
@@ -113,6 +112,11 @@ class _BookDetailPageState extends State<BookDetailPage> {
final screenH = MediaQuery.of(context).size.height;
final hasCover = book.coverPath != null && book.coverPath!.isNotEmpty;
_overlayScrollController ??= ScrollController()..addListener(() {
final show = (_overlayScrollController?.offset ?? 0) > 10;
if (_showTitle.value != show) _showTitle.value = show;
});
return Scaffold(
body: Stack(
children: [
@@ -142,7 +146,7 @@ class _BookDetailPageState extends State<BookDetailPage> {
SafeArea(
child: Column(
children: [
// 顶部栏
// 顶部栏:只在滚动后显示标题
SizedBox(
height: 48,
child: Row(children: [
@@ -151,9 +155,19 @@ class _BookDetailPageState extends State<BookDetailPage> {
icon: const Icon(Icons.arrow_back_ios_new, color: Colors.white, size: 18),
onPressed: () => Navigator.pop(context),
),
const Spacer(),
Text(book.title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
maxLines: 1, overflow: TextOverflow.ellipsis),
ValueListenableBuilder<bool>(
valueListenable: _showTitle,
builder: (_, show, __) => AnimatedOpacity(
opacity: show ? 1.0 : 0.0,
duration: const Duration(milliseconds: 200),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.5),
child: Text(book.title,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
maxLines: 1, overflow: TextOverflow.ellipsis),
),
),
),
const Spacer(),
_buildStyleButton(),
]),
@@ -161,6 +175,7 @@ class _BookDetailPageState extends State<BookDetailPage> {
// 可滚动内容
Expanded(
child: SingleChildScrollView(
controller: _overlayScrollController,
padding: const EdgeInsets.fromLTRB(16, 8, 16, 100),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -485,33 +500,34 @@ class _BookDetailPageState extends State<BookDetailPage> {
void _showStylePicker() {
final colors = Theme.of(context).colorScheme;
const names = ['默认样式', '毛玻璃层叠'];
const icons = [Icons.article_outlined, Icons.blur_on_outlined];
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片'];
showModalBottomSheet(
context: context,
backgroundColor: colors.surface,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
builder: (ctx) => Padding(
padding: const EdgeInsets.fromLTRB(20, 16, 20, 24),
child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [
Text('详情页样式', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: colors.onSurface)),
const SizedBox(height: 16),
Wrap(spacing: 10, runSpacing: 10, children: List.generate(names.length, (i) {
final selected = _detailStyle == i;
return GestureDetector(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Container(width: 36, height: 4, decoration: BoxDecoration(color: colors.onSurface.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(2))),
const SizedBox(height: 20),
Align(alignment: Alignment.centerLeft, child: Text('详情页样式', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: colors.onSurface))),
const SizedBox(height: 12),
for (int i = 0; i < names.length; i++) ...[
if (i > 0) Divider(height: 0.5, color: colors.outlineVariant),
ListTile(
contentPadding: EdgeInsets.zero,
leading: Container(width: 36, height: 36, decoration: BoxDecoration(color: colors.surfaceContainerHighest, borderRadius: BorderRadius.circular(10)),
child: Icon(icons[i], size: 20, color: _detailStyle == i ? colors.primary : colors.onSurface.withValues(alpha: 0.6))),
title: Text(names[i], style: TextStyle(fontSize: 13, fontWeight: _detailStyle == i ? FontWeight.w600 : FontWeight.w500, color: colors.onSurface)),
subtitle: Text(subtitles[i], style: TextStyle(fontSize: 11, color: colors.onSurface.withValues(alpha: 0.4))),
trailing: _detailStyle == i
? Icon(Icons.check_circle, size: 20, color: colors.primary)
: Icon(Icons.chevron_right, color: colors.onSurface.withValues(alpha: 0.25)),
onTap: () { setState(() => _detailStyle = i); UserPrefs().setDetailPageStyle(i); Navigator.pop(ctx); },
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 10),
decoration: BoxDecoration(
color: selected ? colors.primary : colors.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: selected ? colors.primary : colors.outline, width: 0.5),
),
child: Text(names[i], style: TextStyle(
fontSize: 14, fontWeight: selected ? FontWeight.w600 : FontWeight.w400,
color: selected ? colors.onPrimary : colors.onSurface.withValues(alpha: 0.6),
)),
),
);
})),
),
],
const SizedBox(height: 12),
]),
),
);

File diff suppressed because it is too large Load Diff

View File

@@ -25,8 +25,6 @@ class _BookTabPageState extends State<BookTabPage> {
int _offset = 0;
int _lastStatusIndex = -1;
bool _initialized = false;
int _lastDataCount = -1;
DateTime? _lastUpdatedAt;
late ScrollController _scrollController;
AppProvider? _provider;
int _lastScrollSignal = 0;
@@ -42,8 +40,6 @@ class _BookTabPageState extends State<BookTabPage> {
final provider = context.read<AppProvider>();
_provider = provider;
provider.addListener(_onDataChanged);
_lastDataCount = provider.books.length;
if (provider.books.isNotEmpty) _lastUpdatedAt = provider.books.first.updatedAt;
_loadFirst();
});
}
@@ -67,15 +63,8 @@ class _BookTabPageState extends State<BookTabPage> {
}
}
final count = provider.books.length;
final latest = provider.books.isNotEmpty ? provider.books.first.updatedAt : null;
if (count != _lastDataCount || latest != _lastUpdatedAt) {
_lastDataCount = count;
_lastUpdatedAt = latest;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) _loadFirst();
});
}
// 数据变化时刷新列表(排序/评分/新增等)
_loadFirst();
}
void _onScroll() {
@@ -91,7 +80,7 @@ class _BookTabPageState extends State<BookTabPage> {
_initialized = true;
final status = _statusMap[statusIdx] ?? 'read';
setState(() { _isLoading = true; _offset = 0; _hasMore = true; });
final list = await provider.loadBooksPaged(status: status, offset: 0);
final list = await provider.loadBooksPaged(status: status, offset: 0, sortMode: UserPrefs().bookSortMode);
if (!mounted) return;
setState(() { _items.clear(); _items.addAll(list); _offset = list.length; _hasMore = list.length >= 20; _isLoading = false; });
}
@@ -101,7 +90,7 @@ class _BookTabPageState extends State<BookTabPage> {
setState(() => _isLoading = true);
final provider = context.read<AppProvider>();
final status = _statusMap[provider.bookStatusIndex] ?? 'read';
final list = await provider.loadBooksPaged(status: status, offset: _offset);
final list = await provider.loadBooksPaged(status: status, offset: _offset, sortMode: UserPrefs().bookSortMode);
if (!mounted) return;
setState(() { _items.addAll(list); _offset += list.length; _hasMore = list.length >= 20; _isLoading = false; });
}