Merge pull request #594 from lings03/favorite

Optimize favorite page and home page.
This commit is contained in:
ynyx631
2025-11-01 12:29:42 +08:00
committed by GitHub
6 changed files with 173 additions and 84 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,28 +856,30 @@ 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 = var isSelected = selection == null
selection == null ? false : selection![comics[index]] ?? false; ? 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
? () => onTap!(comics[index], heroIDs[index])
: null,
onLongPressed: onLongPressed != null onLongPressed: onLongPressed != null
? () => onLongPressed!(comics[index]) ? () => onLongPressed!(comics[index], heroIDs[index])
: null, : null,
heroID: heroIDs[index], heroID: heroIDs[index],
); );
@@ -889,19 +891,16 @@ class _SliverGridComics extends StatelessWidget {
duration: const Duration(milliseconds: 150), duration: const Duration(milliseconds: 150),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected color: isSelected
? Theme.of(context) ? Theme.of(
.colorScheme context,
.secondaryContainer ).colorScheme.secondaryContainer.toOpacity(0.72)
.toOpacity(0.72)
: null, : null,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
margin: const EdgeInsets.all(4), margin: const EdgeInsets.all(4),
child: comic, child: comic,
); );
}, }, childCount: comics.length),
childCount: comics.length,
),
gridDelegate: SliverGridDelegateWithComics(), gridDelegate: SliverGridDelegateWithComics(),
); );
} }
@@ -1627,7 +1626,7 @@ class _SMClipper extends CustomClipper<Rect> {
class SimpleComicTile extends StatelessWidget { class SimpleComicTile extends StatelessWidget {
const SimpleComicTile( const SimpleComicTile(
{super.key, required this.comic, this.onTap, this.withTitle = false}); {super.key, required this.comic, this.onTap, this.withTitle = false, this.heroID});
final Comic comic; final Comic comic;
@@ -1635,6 +1634,8 @@ class SimpleComicTile extends StatelessWidget {
final bool withTitle; final bool withTitle;
final int? heroID;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var image = _findImageProvider(comic); var image = _findImageProvider(comic);
@@ -1660,6 +1661,13 @@ class SimpleComicTile extends StatelessWidget {
child: child, child: child,
); );
if (heroID != null) {
child = Hero(
tag: "cover$heroID",
child: child,
);
}
child = AnimatedTapRegion( child = AnimatedTapRegion(
borderRadius: 8, borderRadius: 8,
onTap: onTap ?? onTap: onTap ??
@@ -1668,6 +1676,9 @@ class SimpleComicTile extends StatelessWidget {
() => ComicPage( () => ComicPage(
id: comic.id, id: comic.id,
sourceKey: comic.sourceKey, sourceKey: comic.sourceKey,
cover: comic.cover,
title: comic.title,
heroID: heroID,
), ),
); );
}, },

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,15 +348,20 @@ class _AppScrollBarState extends State<AppScrollBar> {
Positioned( Positioned(
top: top + widget.topPadding, top: top + widget.topPadding,
right: 0, right: 0,
child: AnimatedOpacity(
opacity: _isVisible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 200),
child: MouseRegion( child: MouseRegion(
cursor: SystemMouseCursors.click, cursor: SystemMouseCursors.click,
onEnter: (_) => _showScrollbar(),
onExit: (_) => _scheduleHide(),
child: Listener( child: Listener(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
onPointerDown: (event) { onPointerDown: (event) {
_dragGestureRecognizer.addPointer(event); _dragGestureRecognizer.addPointer(event);
}, },
child: SizedBox( child: SizedBox(
width: _scrollIndicatorSize/2, width: _scrollIndicatorSize / 2,
height: _scrollIndicatorSize, height: _scrollIndicatorSize,
child: CustomPaint( child: CustomPaint(
painter: _ScrollIndicatorPainter( painter: _ScrollIndicatorPainter(
@@ -328,6 +381,7 @@ class _AppScrollBarState extends State<AppScrollBar> {
), ),
), ),
), ),
),
], ],
); );
}, },

View File

@@ -689,7 +689,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)) {
@@ -701,20 +701,24 @@ 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,
), ),
enableIOSGesture: false, enableIOSGesture: false,
iosFullScreenGesture: false, iosFullScreenGesture: false,
); );
} 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

@@ -302,13 +302,18 @@ class _HistoryState extends State<_History> {
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: history.length, itemCount: history.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final heroID = history[index].id.hashCode;
return SimpleComicTile( return SimpleComicTile(
comic: history[index], comic: history[index],
heroID: heroID,
onTap: () { onTap: () {
context.to( context.to(
() => ComicPage( () => ComicPage(
id: history[index].id, id: history[index].id,
sourceKey: history[index].type.sourceKey, sourceKey: history[index].type.sourceKey,
cover: history[index].cover,
title: history[index].title,
heroID: heroID,
), ),
); );
}, },
@@ -386,7 +391,9 @@ class _LocalState extends State<_Local> {
Container( Container(
margin: const EdgeInsets.symmetric(horizontal: 8), margin: const EdgeInsets.symmetric(horizontal: 8),
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 2), horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondaryContainer, color: Theme.of(context).colorScheme.secondaryContainer,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
@@ -405,9 +412,22 @@ class _LocalState extends State<_Local> {
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: local.length, itemCount: local.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return SimpleComicTile(comic: local[index]) final heroID = local[index].id.hashCode;
.paddingHorizontal(8) return SimpleComicTile(
.paddingVertical(2); comic: local[index],
heroID: heroID,
onTap: () {
context.to(
() => ComicPage(
id: local[index].id,
sourceKey: local[index].sourceKey,
cover: local[index].cover,
title: local[index].title,
heroID: heroID,
),
);
},
).paddingHorizontal(8).paddingVertical(2);
}, },
), ),
).paddingHorizontal(8), ).paddingHorizontal(8),

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)) {