mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-06-06 05:49:03 +08:00
feat: 添加 ScrollController 支持到画廊列表卡片和画廊页面,改善滚动体验
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user