feat: 添加 ScrollController 支持到画廊列表卡片和画廊页面,改善滚动体验

This commit is contained in:
2025-03-08 22:23:18 +08:00
parent a80278dffd
commit d737b85076
4 changed files with 113 additions and 69 deletions

View File

@@ -165,7 +165,9 @@ class GalleryInfoDesktop extends StatelessWidget {
)
])),
const VerticalDivider(indent: 10, endIndent: 10),
Expanded(child: TagsPanel(gData.tags)),
Expanded(
child:
TagsPanel(gData.tags, controller: controller)),
const VerticalDivider(indent: 10, endIndent: 10),
SizedBox(
width: 150,

View File

@@ -1,3 +1,4 @@
import 'dart:math';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
@@ -6,12 +7,17 @@ import '../api/file.dart';
import '../api/gallery.dart';
import '../globals.dart';
import '../main.dart';
import '../platform/ua.dart' as ua;
import '../utils.dart';
import 'rate.dart';
import 'scroll_parent.dart';
import 'thumbnail.dart';
class GalleryListNormalCard extends StatefulWidget {
const GalleryListNormalCard(this.gMeta, {super.key, this.files, this.pMeta});
const GalleryListNormalCard(this.gMeta,
{super.key, this.controller, this.files, this.pMeta});
final GMeta gMeta;
final ScrollController? controller;
final ExtendedPMeta? pMeta;
final EhFiles? files;
@@ -52,42 +58,66 @@ class _GalleryListNormalCard extends State<GalleryListNormalCard> {
? EdgeInsets.symmetric(vertical: 2 / dpr, horizontal: 4 / dpr)
: EdgeInsets.all(8 / dpr),
child: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
SelectableText(
useMobile ? widget.gMeta.preferredTitle : widget.gMeta.title,
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16, color: cs.primary),
textAlign: TextAlign.center,
minLines: useMobile ? 1 : 1,
maxLines: useMobile
? 3
: widget.gMeta.titleJpn.isEmpty
? 4
: 2),
ScrollParent(
controller: widget.controller,
child: SelectableText(
useMobile ? widget.gMeta.preferredTitle : widget.gMeta.title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: cs.primary),
textAlign: TextAlign.center,
minLines: useMobile ? 1 : 1,
maxLines: useMobile
? 3
: widget.gMeta.titleJpn.isEmpty
? 4
: 2,
scrollPhysics: isIOS || ua.isSafari
? const ClampingScrollPhysics()
: null)),
useMobile || widget.gMeta.titleJpn.isEmpty
? Container()
: SelectableText(
widget.gMeta.titleJpn,
style: TextStyle(color: cs.secondary),
textAlign: TextAlign.center,
maxLines: 2,
minLines: 1,
),
: ScrollParent(
controller: widget.controller,
child: SelectableText(widget.gMeta.titleJpn,
style: TextStyle(color: cs.secondary),
textAlign: TextAlign.center,
maxLines: 2,
minLines: 1,
scrollPhysics: isIOS || ua.isSafari
? const ClampingScrollPhysics()
: null)),
Expanded(child: Container()),
Row(crossAxisAlignment: CrossAxisAlignment.end, children: [
SelectableText.rich(TextSpan(
text: widget.gMeta.uploader,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: cs.primary),
mouseCursor: SystemMouseCursors.click,
recognizer: TapGestureRecognizer()
..onTap = () {
context.pushNamed("/galleries",
queryParameters: {"uploader": widget.gMeta.uploader});
})),
]),
Row(crossAxisAlignment: CrossAxisAlignment.end, children: [
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
SelectableText.rich(TextSpan(
text: widget.gMeta.uploader,
Rate(widget.gMeta.rating, fontSize: 14, selectable: true),
]),
Expanded(child: Container()),
Column(crossAxisAlignment: CrossAxisAlignment.end, children: [
SelectableText("${widget.gMeta.filecount}P",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: cs.primary),
mouseCursor: SystemMouseCursors.click,
recognizer: TapGestureRecognizer()
..onTap = () {
context.pushNamed("/galleries",
queryParameters: {"uploader": widget.gMeta.uploader});
})),
Rate(widget.gMeta.rating, fontSize: 14, selectable: true),
color: cs.primary,
fontWeight: FontWeight.bold)),
]),
]),
Row(crossAxisAlignment: CrossAxisAlignment.end, children: [
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
SelectableText.rich(TextSpan(
text: widget.gMeta.category,
style: TextStyle(
@@ -103,11 +133,6 @@ class _GalleryListNormalCard extends State<GalleryListNormalCard> {
]),
Expanded(child: Container()),
Column(crossAxisAlignment: CrossAxisAlignment.end, children: [
SelectableText("${widget.gMeta.filecount}P",
style: TextStyle(
fontSize: 16,
color: cs.primary,
fontWeight: FontWeight.bold)),
SelectableText(
DateFormat.yMd(locale)
.add_jms()
@@ -127,7 +152,8 @@ class _GalleryListNormalCard extends State<GalleryListNormalCard> {
child: Row(children: [
useMobile
? ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxWidth / 2),
constraints: BoxConstraints(
maxWidth: min(maxWidth / 2, maxWidth - 260)),
child: thumbnailWidget)
: thumbnailWidget,
Expanded(child: mainWidget),

View File

@@ -1,27 +1,29 @@
import 'package:flutter/material.dart';
class ScrollParent extends StatelessWidget {
final ScrollController controller;
final ScrollController? controller;
final Widget child;
const ScrollParent({super.key, required this.controller, required this.child});
const ScrollParent({super.key, this.controller, required this.child});
@override
Widget build(BuildContext context) {
if (controller == null) return child;
return NotificationListener<OverscrollNotification>(
onNotification: (OverscrollNotification value) {
if (value.overscroll < 0 && controller.offset + value.overscroll <= 0) {
if (controller.offset != 0) controller.jumpTo(0);
if (value.overscroll < 0 &&
controller!.offset + value.overscroll <= 0) {
if (controller!.offset != 0) controller!.jumpTo(0);
return true;
}
if (controller.offset + value.overscroll >=
controller.position.maxScrollExtent) {
if (controller.offset != controller.position.maxScrollExtent) {
controller.jumpTo(controller.position.maxScrollExtent);
if (controller!.offset + value.overscroll >=
controller!.position.maxScrollExtent) {
if (controller!.offset != controller!.position.maxScrollExtent) {
controller!.jumpTo(controller!.position.maxScrollExtent);
}
return true;
}
controller.jumpTo(controller.offset + value.overscroll);
controller!.jumpTo(controller!.offset + value.overscroll);
return true;
},
child: child,

View File

@@ -1,3 +1,4 @@
import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -45,6 +46,7 @@ class GalleriesPage extends StatefulWidget {
class _GalleriesPage extends State<GalleriesPage>
with ThemeModeWidget, IsTopWidget2 {
final ScrollController controller = ScrollController();
static const int _pageSize = 20;
bool? _sortByGid;
SortByGid _sortByGid2 = SortByGid.none;
@@ -229,33 +231,45 @@ class _GalleriesPage extends State<GalleriesPage>
buildThemeModeIcon(context),
buildMoreVertSettingsButon(context),
]),
body: PagedListView<int, GMeta>(
physics: const AlwaysScrollableScrollPhysics(),
pagingController: _pagingController,
builderDelegate: PagedChildBuilderDelegate<GMeta>(
itemBuilder: (context, item, index) {
final displayMode = GalleryListDisplayMode
.values[prefs.getInt("galleryListDisplayMode") ?? 1];
if (displayMode == GalleryListDisplayMode.normal) {
return InkWell(
body: ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(
dragDevices: {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
PointerDeviceKind.trackpad,
},
),
child: PagedListView<int, GMeta>(
physics: const AlwaysScrollableScrollPhysics(),
pagingController: _pagingController,
scrollController: controller,
builderDelegate: PagedChildBuilderDelegate<GMeta>(
itemBuilder: (context, item, index) {
final displayMode = GalleryListDisplayMode
.values[prefs.getInt("galleryListDisplayMode") ?? 1];
if (displayMode == GalleryListDisplayMode.normal) {
return InkWell(
onTap: () {
context.push("/gallery/${item.gid}",
extra:
GalleryPageExtra(title: item.preferredTitle));
},
mouseCursor: SystemMouseCursors.basic,
child: GalleryListNormalCard(item,
controller: controller,
files: _files,
pMeta: _thumbnails.thumbnails[item.gid]
?.unwrapOrNull()));
}
return ListTile(
title: Text(item.preferredTitle),
onTap: () {
context.push("/gallery/${item.gid}",
extra: GalleryPageExtra(title: item.preferredTitle));
},
mouseCursor: SystemMouseCursors.basic,
child: GalleryListNormalCard(item,
files: _files,
pMeta: _thumbnails.thumbnails[item.gid]?.unwrapOrNull()));
}
return ListTile(
title: Text(item.preferredTitle),
onTap: () {
context.push("/gallery/${item.gid}",
extra: GalleryPageExtra(title: item.preferredTitle));
},
);
}),
));
);
}),
)));
}
@override