Optimize favorite page.

This commit is contained in:
角砂糖
2025-10-26 21:01:52 +08:00
parent 09a1d2821c
commit 39a834815d
5 changed files with 136 additions and 79 deletions

View File

@@ -753,9 +753,9 @@ class SliverGridComics extends StatefulWidget {
final List<MenuEntry> Function(Comic)? menuBuilder; final List<MenuEntry> Function(Comic)? menuBuilder;
final void Function(Comic)? onTap; final void Function(Comic, int heroID)? onTap;
final void Function(Comic)? onLongPressed; final void Function(Comic, int heroID)? onLongPressed;
@override @override
State<SliverGridComics> createState() => _SliverGridComicsState(); State<SliverGridComics> createState() => _SliverGridComicsState();
@@ -856,52 +856,51 @@ class _SliverGridComics extends StatelessWidget {
final List<MenuEntry> Function(Comic)? menuBuilder; final List<MenuEntry> Function(Comic)? menuBuilder;
final void Function(Comic)? onTap; final void Function(Comic, int heroID)? onTap;
final void Function(Comic)? onLongPressed; final void Function(Comic, int heroID)? onLongPressed;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SliverGrid( return SliverGrid(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate((context, index) {
(context, index) { if (index == comics.length - 1) {
if (index == comics.length - 1) { onLastItemBuild?.call();
onLastItemBuild?.call(); }
} var badge = badgeBuilder?.call(comics[index]);
var badge = badgeBuilder?.call(comics[index]); var isSelected = selection == null
var isSelected = ? false
selection == null ? false : selection![comics[index]] ?? false; : selection![comics[index]] ?? false;
var comic = ComicTile( var comic = ComicTile(
comic: comics[index], comic: comics[index],
badge: badge, badge: badge,
menuOptions: menuBuilder?.call(comics[index]), menuOptions: menuBuilder?.call(comics[index]),
onTap: onTap != null ? () => onTap!(comics[index]) : null, onTap: onTap != null
onLongPressed: onLongPressed != null ? () => onTap!(comics[index], heroIDs[index])
? () => onLongPressed!(comics[index]) : null,
onLongPressed: onLongPressed != null
? () => onLongPressed!(comics[index], heroIDs[index])
: null,
heroID: heroIDs[index],
);
if (selection == null) {
return comic;
}
return AnimatedContainer(
key: ValueKey(comics[index].id),
duration: const Duration(milliseconds: 150),
decoration: BoxDecoration(
color: isSelected
? Theme.of(
context,
).colorScheme.secondaryContainer.toOpacity(0.72)
: null, : null,
heroID: heroIDs[index], borderRadius: BorderRadius.circular(12),
); ),
if (selection == null) { margin: const EdgeInsets.all(4),
return comic; child: comic,
} );
return AnimatedContainer( }, childCount: comics.length),
key: ValueKey(comics[index].id),
duration: const Duration(milliseconds: 150),
decoration: BoxDecoration(
color: isSelected
? Theme.of(context)
.colorScheme
.secondaryContainer
.toOpacity(0.72)
: null,
borderRadius: BorderRadius.circular(12),
),
margin: const EdgeInsets.all(4),
child: comic,
);
},
childCount: comics.length,
),
gridDelegate: SliverGridDelegateWithComics(), gridDelegate: SliverGridDelegateWithComics(),
); );
} }

View File

@@ -241,6 +241,10 @@ class _AppScrollBarState extends State<AppScrollBar> {
late final VerticalDragGestureRecognizer _dragGestureRecognizer; late final VerticalDragGestureRecognizer _dragGestureRecognizer;
bool _isVisible = false;
Timer? _hideTimer;
static const _hideDuration = Duration(seconds: 2);
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@@ -248,7 +252,41 @@ class _AppScrollBarState extends State<AppScrollBar> {
_scrollController.addListener(onChanged); _scrollController.addListener(onChanged);
Future.microtask(onChanged); Future.microtask(onChanged);
_dragGestureRecognizer = VerticalDragGestureRecognizer() _dragGestureRecognizer = VerticalDragGestureRecognizer()
..onUpdate = onUpdate; ..onUpdate = onUpdate
..onStart = (_) {
_showScrollbar();
}
..onEnd = (_) {
_scheduleHide();
};
}
@override
void dispose() {
_hideTimer?.cancel();
_scrollController.removeListener(onChanged);
_dragGestureRecognizer.dispose();
super.dispose();
}
void _showScrollbar() {
if (!_isVisible && mounted) {
setState(() {
_isVisible = true;
});
}
_hideTimer?.cancel();
}
void _scheduleHide() {
_hideTimer?.cancel();
_hideTimer = Timer(_hideDuration, () {
if (mounted && _isVisible) {
setState(() {
_isVisible = false;
});
}
});
} }
void onUpdate(DragUpdateDetails details) { void onUpdate(DragUpdateDetails details) {
@@ -269,14 +307,24 @@ class _AppScrollBarState extends State<AppScrollBar> {
void onChanged() { void onChanged() {
if (_scrollController.positions.isEmpty) return; if (_scrollController.positions.isEmpty) return;
var position = _scrollController.position; var position = _scrollController.position;
bool hasChanged = false;
if (position.minScrollExtent != minExtent || if (position.minScrollExtent != minExtent ||
position.maxScrollExtent != maxExtent || position.maxScrollExtent != maxExtent ||
position.pixels != this.position) { position.pixels != this.position) {
setState(() { hasChanged = true;
minExtent = position.minScrollExtent; minExtent = position.minScrollExtent;
maxExtent = position.maxScrollExtent; maxExtent = position.maxScrollExtent;
this.position = position.pixels; this.position = position.pixels;
}); }
if (hasChanged) {
_showScrollbar();
_scheduleHide();
}
if (hasChanged && mounted) {
setState(() {});
} }
} }
@@ -300,29 +348,35 @@ class _AppScrollBarState extends State<AppScrollBar> {
Positioned( Positioned(
top: top + widget.topPadding, top: top + widget.topPadding,
right: 0, right: 0,
child: MouseRegion( child: AnimatedOpacity(
cursor: SystemMouseCursors.click, opacity: _isVisible ? 1.0 : 0.0,
child: Listener( duration: const Duration(milliseconds: 200),
behavior: HitTestBehavior.translucent, child: MouseRegion(
onPointerDown: (event) { cursor: SystemMouseCursors.click,
_dragGestureRecognizer.addPointer(event); onEnter: (_) => _showScrollbar(),
}, onExit: (_) => _scheduleHide(),
child: SizedBox( child: Listener(
width: _scrollIndicatorSize/2, behavior: HitTestBehavior.translucent,
height: _scrollIndicatorSize, onPointerDown: (event) {
child: CustomPaint( _dragGestureRecognizer.addPointer(event);
painter: _ScrollIndicatorPainter( },
backgroundColor: context.colorScheme.surface, child: SizedBox(
shadowColor: context.colorScheme.shadow, width: _scrollIndicatorSize / 2,
height: _scrollIndicatorSize,
child: CustomPaint(
painter: _ScrollIndicatorPainter(
backgroundColor: context.colorScheme.surface,
shadowColor: context.colorScheme.shadow,
),
child: Column(
children: [
const Spacer(),
Icon(Icons.arrow_drop_up, size: 18),
Icon(Icons.arrow_drop_down, size: 18),
const Spacer(),
],
).paddingLeft(4),
), ),
child: Column(
children: [
const Spacer(),
Icon(Icons.arrow_drop_up, size: 18),
Icon(Icons.arrow_drop_down, size: 18),
const Spacer(),
],
).paddingLeft(4),
), ),
), ),
), ),

View File

@@ -627,7 +627,7 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
), ),
]; ];
}, },
onTap: (c) { onTap: (c, heroID) {
if (multiSelectMode) { if (multiSelectMode) {
setState(() { setState(() {
if (selectedComics.containsKey(c as FavoriteItem)) { if (selectedComics.containsKey(c as FavoriteItem)) {
@@ -639,18 +639,22 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
lastSelectedIndex = comics.indexOf(c); lastSelectedIndex = comics.indexOf(c);
}); });
} else if (appdata.settings["onClickFavorite"] == "viewDetail") { } else if (appdata.settings["onClickFavorite"] == "viewDetail") {
App.mainNavigatorKey?.currentContext
?.to(() => ComicPage(id: c.id, sourceKey: c.sourceKey));
} else {
App.mainNavigatorKey?.currentContext?.to( App.mainNavigatorKey?.currentContext?.to(
() => ReaderWithLoading( () => ComicPage(
id: c.id, id: c.id,
sourceKey: c.sourceKey, sourceKey: c.sourceKey,
cover: c.cover,
title: c.title,
heroID: heroID,
), ),
); );
} else {
App.mainNavigatorKey?.currentContext?.to(
() => ReaderWithLoading(id: c.id, sourceKey: c.sourceKey),
);
} }
}, },
onLongPressed: (c) { onLongPressed: (c, heroID) {
setState(() { setState(() {
if (!multiSelectMode) { if (!multiSelectMode) {
multiSelectMode = true; multiSelectMode = true;

View File

@@ -211,7 +211,7 @@ class _HistoryPageState extends State<HistoryPage> {
selections: selectedComics, selections: selectedComics,
onLongPressed: null, onLongPressed: null,
onTap: multiSelectMode onTap: multiSelectMode
? (c) { ? (c, heroID) {
setState(() { setState(() {
if (selectedComics.containsKey(c as History)) { if (selectedComics.containsKey(c as History)) {
selectedComics.remove(c); selectedComics.remove(c);

View File

@@ -285,13 +285,13 @@ class _LocalComicsPageState extends State<LocalComicsPage> {
SliverGridComics( SliverGridComics(
comics: comics, comics: comics,
selections: selectedComics, selections: selectedComics,
onLongPressed: (c) { onLongPressed: (c, heroID) {
setState(() { setState(() {
multiSelectMode = true; multiSelectMode = true;
selectedComics[c as LocalComic] = true; selectedComics[c as LocalComic] = true;
}); });
}, },
onTap: (c) { onTap: (c, heroID) {
if (multiSelectMode) { if (multiSelectMode) {
setState(() { setState(() {
if (selectedComics.containsKey(c as LocalComic)) { if (selectedComics.containsKey(c as LocalComic)) {