Files
venera/lib/components/scroll.dart
luckyray d874920c88 Feat: Image favorites (#126)
* feat: 增加图片收藏

* feat: 主体图片收藏页面实现

* feat: 点击打开大图浏览

* feat: 数据结构变更

* feat: 基本完成

* feat: 翻译与bug修复

* feat: 实机测试和问题修复

* feat: jm导入, pica历史记录nhentai有问题, 一键反转

* fix: 大小写不一致, 一个htManga, 一个htmanga

* feat: 拉取收藏优化

* feat: 改成以ep为准

* feat: 兜底一些可能报错场景

* chore: 没有用到

* feat: 尽量保证和网络收藏顺序一致

* feat: 支持显示热点tag

* feat: 支持双击收藏, 不过此时禁止放大图片

* fix: 自动塞封面逻辑完善, 切换快速收藏图片立刻生效

* Refactor

* fix updateValue

* feat: 双击功能提示

* fix: 被确定取消收藏的才删除

* Refactor ImageFavoritesPage

* translate author

* feat: 功能提示改到dialog中

* fix text editing

* fix text editing

* feat: 功能提示放到邮件或长按菜单中

* fix: 修复tag过滤不生效问题

* Improve image loading

* The default value of quickCollectImage should be false.

* Refactor DragListener

* Refactor ImageFavoriteItem & ImageFavoritePhotoView

* Refactor

* Fix `ImageFavoriteManager.has`

* Fix UI

* Improve UI

---------

Co-authored-by: nyne <me@nyne.dev>
2025-01-15 16:07:08 +08:00

138 lines
3.8 KiB
Dart

part of 'components.dart';
class SmoothCustomScrollView extends StatelessWidget {
const SmoothCustomScrollView(
{super.key, required this.slivers, this.controller});
final ScrollController? controller;
final List<Widget> slivers;
@override
Widget build(BuildContext context) {
return SmoothScrollProvider(
controller: controller,
builder: (context, controller, physics) {
return CustomScrollView(
controller: controller,
physics: physics,
slivers: [
...slivers,
SliverPadding(
padding: EdgeInsets.only(
bottom: context.padding.bottom,
),
),
],
);
},
);
}
}
class SmoothScrollProvider extends StatefulWidget {
const SmoothScrollProvider(
{super.key, this.controller, required this.builder});
final ScrollController? controller;
final Widget Function(BuildContext, ScrollController, ScrollPhysics) builder;
static bool get isMouseScroll => _SmoothScrollProviderState._isMouseScroll;
@override
State<SmoothScrollProvider> createState() => _SmoothScrollProviderState();
}
class _SmoothScrollProviderState extends State<SmoothScrollProvider> {
late final ScrollController _controller;
double? _futurePosition;
static bool _isMouseScroll = App.isDesktop;
@override
void initState() {
_controller = widget.controller ?? ScrollController();
super.initState();
}
@override
Widget build(BuildContext context) {
if (App.isMacOS) {
return widget.builder(
context,
_controller,
const BouncingScrollPhysics(),
);
}
return Listener(
behavior: HitTestBehavior.translucent,
onPointerDown: (event) {
_futurePosition = null;
if (_isMouseScroll) {
setState(() {
_isMouseScroll = false;
});
}
},
onPointerSignal: (pointerSignal) {
if (pointerSignal is PointerScrollEvent) {
if (HardwareKeyboard.instance.isShiftPressed) {
return;
}
if (pointerSignal.kind == PointerDeviceKind.mouse &&
!_isMouseScroll) {
setState(() {
_isMouseScroll = true;
});
}
if (!_isMouseScroll) return;
var currentLocation = _controller.position.pixels;
var old = _futurePosition;
_futurePosition ??= currentLocation;
double k = (_futurePosition! - currentLocation).abs() / 1600 + 1;
_futurePosition = _futurePosition! + pointerSignal.scrollDelta.dy * k;
_futurePosition = _futurePosition!.clamp(
_controller.position.minScrollExtent,
_controller.position.maxScrollExtent,
);
if (_futurePosition == old) return;
_controller.animateTo(_futurePosition!,
duration: _fastAnimationDuration, curve: Curves.linear);
}
},
child: ScrollControllerProvider._(
controller: _controller,
child: widget.builder(
context,
_controller,
_isMouseScroll
? const NeverScrollableScrollPhysics()
: const BouncingScrollPhysics(),
),
),
);
}
}
class ScrollControllerProvider extends InheritedWidget {
const ScrollControllerProvider._({
required this.controller,
required super.child,
});
final ScrollController controller;
static ScrollController of(BuildContext context) {
final ScrollControllerProvider? provider =
context.dependOnInheritedWidgetOfExactType<ScrollControllerProvider>();
return provider!.controller;
}
@override
bool updateShouldNotify(ScrollControllerProvider oldWidget) {
return oldWidget.controller != controller;
}
}