diff --git a/lib/common/widgets/gesture/image_tap_gesture_recognizer.dart b/lib/common/widgets/gesture/image_tap_gesture_recognizer.dart index 2e8fee18b..fa408a37a 100644 --- a/lib/common/widgets/gesture/image_tap_gesture_recognizer.dart +++ b/lib/common/widgets/gesture/image_tap_gesture_recognizer.dart @@ -1,5 +1,5 @@ import 'package:flutter/gestures.dart' - show TapGestureRecognizer, PointerDownEvent; + show TapGestureRecognizer, PointerDownEvent, DoubleTapGestureRecognizer; class ImageTapGestureRecognizer extends TapGestureRecognizer { ImageTapGestureRecognizer({ @@ -21,3 +21,22 @@ class ImageTapGestureRecognizer extends TapGestureRecognizer { super.addPointer(event); } } + +class ImageDoubleTapGestureRecognizer extends DoubleTapGestureRecognizer { + ImageDoubleTapGestureRecognizer({ + super.debugOwner, + super.supportedDevices, + super.allowedButtonsFilter, + }); + + int? _pointer; + + @override + void addPointer(PointerDownEvent event) { + if (_pointer == event.pointer) { + return; + } + _pointer = event.pointer; + super.addPointer(event); + } +} diff --git a/lib/common/widgets/image_viewer/gallery_viewer.dart b/lib/common/widgets/image_viewer/gallery_viewer.dart index f1c35e2eb..3b73cad60 100644 --- a/lib/common/widgets/image_viewer/gallery_viewer.dart +++ b/lib/common/widgets/image_viewer/gallery_viewer.dart @@ -81,6 +81,7 @@ class _GalleryViewerState extends State late final PageController _pageController; late final ImageTapGestureRecognizer _tapGestureRecognizer; + late final ImageDoubleTapGestureRecognizer _doubleTapGestureRecognizer; late final ImageHorizontalDragGestureRecognizer _horizontalDragGestureRecognizer; late final LongPressGestureRecognizer _longPressGestureRecognizer; @@ -122,12 +123,15 @@ class _GalleryViewerState extends State if (PlatformUtils.isDesktop) { _tapGestureRecognizer.onSecondaryTapUp = _showDesktopMenu; } + _doubleTapGestureRecognizer = ImageDoubleTapGestureRecognizer() + ..onDoubleTap = () {} + ..gestureSettings = gestureSettings; _horizontalDragGestureRecognizer = ImageHorizontalDragGestureRecognizer(); _longPressGestureRecognizer = LongPressGestureRecognizer() ..onLongPress = _onLongPress ..gestureSettings = gestureSettings; - Future.delayed(const Duration(milliseconds: 410), () { + Future.delayed(const Duration(milliseconds: 300), () { if (mounted) { _tapGestureRecognizer.onTap = _onTap; } @@ -225,6 +229,10 @@ class _GalleryViewerState extends State _pageController.dispose(); _animateController.dispose(); _tapGestureRecognizer.dispose(); + _doubleTapGestureRecognizer + ..onDoubleTapDown = null + ..onDoubleTap = null + ..dispose(); _longPressGestureRecognizer.dispose(); _currIndex.close(); if (widget.quality != _quality) { @@ -239,6 +247,7 @@ class _GalleryViewerState extends State void _onPointerDown(PointerDownEvent event) { _tapGestureRecognizer.addPointer(event); + _doubleTapGestureRecognizer.addPointer(event); _longPressGestureRecognizer.addPointer(event); } @@ -357,6 +366,7 @@ class _GalleryViewerState extends State onDragUpdate: _onDragUpdate, onDragEnd: _onDragEnd, tapGestureRecognizer: _tapGestureRecognizer, + doubleTapGestureRecognizer: _doubleTapGestureRecognizer, horizontalDragGestureRecognizer: _horizontalDragGestureRecognizer, onChangePage: _onChangePage, ); @@ -369,6 +379,7 @@ class _GalleryViewerState extends State maxScale: widget.maxScale, containerSize: _containerSize, tapGestureRecognizer: _tapGestureRecognizer, + doubleTapGestureRecognizer: _doubleTapGestureRecognizer, horizontalDragGestureRecognizer: _horizontalDragGestureRecognizer, onChangePage: _onChangePage, frameBuilder: (context, child, frame, wasSynchronouslyLoaded) { @@ -390,6 +401,7 @@ class _GalleryViewerState extends State onDragUpdate: null, onDragEnd: null, tapGestureRecognizer: _tapGestureRecognizer, + doubleTapGestureRecognizer: _doubleTapGestureRecognizer, horizontalDragGestureRecognizer: _horizontalDragGestureRecognizer, onChangePage: _onChangePage, @@ -428,6 +440,7 @@ class _GalleryViewerState extends State onDragUpdate: _onDragUpdate, onDragEnd: _onDragEnd, tapGestureRecognizer: _tapGestureRecognizer, + doubleTapGestureRecognizer: _doubleTapGestureRecognizer, horizontalDragGestureRecognizer: _horizontalDragGestureRecognizer, onChangePage: _onChangePage, diff --git a/lib/common/widgets/image_viewer/image.dart b/lib/common/widgets/image_viewer/image.dart index 2c82e5a9e..60b07cada 100644 --- a/lib/common/widgets/image_viewer/image.dart +++ b/lib/common/widgets/image_viewer/image.dart @@ -43,6 +43,7 @@ class Image extends StatefulWidget { required this.onDragUpdate, required this.onDragEnd, required this.tapGestureRecognizer, + required this.doubleTapGestureRecognizer, required this.horizontalDragGestureRecognizer, required this.onChangePage, }); @@ -81,6 +82,7 @@ class Image extends StatefulWidget { required this.onDragUpdate, required this.onDragEnd, required this.tapGestureRecognizer, + required this.doubleTapGestureRecognizer, required this.horizontalDragGestureRecognizer, required this.onChangePage, }) : image = ResizeImage.resizeIfNeeded( @@ -126,6 +128,7 @@ class Image extends StatefulWidget { required this.onDragUpdate, required this.onDragEnd, required this.tapGestureRecognizer, + required this.doubleTapGestureRecognizer, required this.horizontalDragGestureRecognizer, required this.onChangePage, }) : assert( @@ -174,6 +177,7 @@ class Image extends StatefulWidget { required this.onDragUpdate, required this.onDragEnd, required this.tapGestureRecognizer, + required this.doubleTapGestureRecognizer, required this.horizontalDragGestureRecognizer, required this.onChangePage, }) : image = ResizeImage.resizeIfNeeded( @@ -222,6 +226,7 @@ class Image extends StatefulWidget { required this.onDragUpdate, required this.onDragEnd, required this.tapGestureRecognizer, + required this.doubleTapGestureRecognizer, required this.horizontalDragGestureRecognizer, required this.onChangePage, }) : image = ResizeImage.resizeIfNeeded( @@ -281,6 +286,7 @@ class Image extends StatefulWidget { final ValueChanged? onChangePage; final ImageTapGestureRecognizer tapGestureRecognizer; + final ImageDoubleTapGestureRecognizer doubleTapGestureRecognizer; final ImageHorizontalDragGestureRecognizer horizontalDragGestureRecognizer; @override @@ -625,6 +631,7 @@ class _ImageState extends State with WidgetsBindingObserver { onDragUpdate: widget.onDragUpdate, onDragEnd: widget.onDragEnd, tapGestureRecognizer: widget.tapGestureRecognizer, + doubleTapGestureRecognizer: widget.doubleTapGestureRecognizer, horizontalDragGestureRecognizer: widget.horizontalDragGestureRecognizer, onChangePage: widget.onChangePage, child: RawImage(image: _imageInfo?.image), diff --git a/lib/common/widgets/image_viewer/viewer.dart b/lib/common/widgets/image_viewer/viewer.dart index 6ae02c4f1..c52624a86 100644 --- a/lib/common/widgets/image_viewer/viewer.dart +++ b/lib/common/widgets/image_viewer/viewer.dart @@ -28,9 +28,6 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/physics.dart' show FrictionSimulation; import 'package:flutter/services.dart' show HardwareKeyboard; -import 'package:get/get_core/src/get_main.dart'; -import 'package:get/get_navigation/src/extension_navigation.dart' - show GetNavigation; /// /// created by dom on 2026/02/14 @@ -48,6 +45,7 @@ class Viewer extends StatefulWidget { required this.onDragUpdate, required this.onDragEnd, required this.tapGestureRecognizer, + required this.doubleTapGestureRecognizer, required this.horizontalDragGestureRecognizer, required this.onChangePage, required this.child, @@ -66,6 +64,7 @@ class Viewer extends StatefulWidget { final ValueChanged? onChangePage; final ImageTapGestureRecognizer tapGestureRecognizer; + final ImageDoubleTapGestureRecognizer doubleTapGestureRecognizer; final ImageHorizontalDragGestureRecognizer horizontalDragGestureRecognizer; @override @@ -87,18 +86,13 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { late Size _imageSize; late final ImageTapGestureRecognizer _tapGestureRecognizer; + late final ImageDoubleTapGestureRecognizer _doubleTapGestureRecognizer; late final ImageHorizontalDragGestureRecognizer _horizontalDragGestureRecognizer; late final ScaleGestureRecognizer _scaleGestureRecognizer; - late final DoubleTapGestureRecognizer _doubleTapGestureRecognizer; Offset? _downPos; - AnimationController? _animationController; - AnimationController get _effectiveAnimationController => - _animationController ??= AnimationController( - vsync: this, - duration: const Duration(milliseconds: 300), - )..addListener(_listener); + late final AnimationController _animationController; late double _scaleFrom, _scaleTo; late Offset _positionFrom, _positionTo; @@ -108,7 +102,7 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { ..scaleByDouble(_scale, _scale, _scale, 1.0); void _listener() { - final t = Curves.easeOut.transform(_effectiveAnimationController.value); + final t = Curves.easeOut.transform(_animationController.value); _scale = t.lerp(_scaleFrom, _scaleTo); _position = Offset.lerp(_positionFrom, _positionTo, t)!; setState(() {}); @@ -142,7 +136,13 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { super.initState(); _initSize(); + _animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + )..addListener(_listener); + _tapGestureRecognizer = widget.tapGestureRecognizer; + _doubleTapGestureRecognizer = widget.doubleTapGestureRecognizer; _horizontalDragGestureRecognizer = widget.horizontalDragGestureRecognizer; _scaleGestureRecognizer = ScaleGestureRecognizer(debugOwner: this) @@ -151,10 +151,6 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { ..onUpdate = _onScaleUpdate ..onEnd = _onScaleEnd ..gestureSettings = DeviceGestureSettings(touchSlop: touchSlopH); - _doubleTapGestureRecognizer = DoubleTapGestureRecognizer(debugOwner: this) - ..onDoubleTapDown = _onDoubleTapDown - ..onDoubleTap = _onDoubleTap - ..gestureSettings = MediaQuery.maybeGestureSettingsOf(Get.context!); } @override @@ -169,11 +165,9 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { @override void dispose() { _animationController - ?..removeListener(_listener) + ..removeListener(_listener) ..dispose(); - _animationController = null; _scaleGestureRecognizer.dispose(); - _doubleTapGestureRecognizer.dispose(); super.dispose(); } @@ -221,6 +215,8 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { } void _onDoubleTap() { + if (!mounted) return; + if (_animationController.isAnimating) return; EasyThrottle.throttle( 'VIEWER_TAP', const Duration(milliseconds: 555), @@ -229,7 +225,8 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { } void _handleDoubleTap() { - if (_effectiveAnimationController.isAnimating) return; + if (!mounted) return; + if (_animationController.isAnimating) return; _scaleFrom = _scale; _positionFrom = _position; @@ -250,7 +247,7 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { _scaleTo = endScale; _positionTo = position; - _effectiveAnimationController + _animationController ..duration = const Duration(milliseconds: 300) ..forward(from: 0); } @@ -261,6 +258,10 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { } void _onScaleStart(ScaleStartDetails details) { + if (_animationController.isAnimating) { + _animationController.stop(); + } + if (details.pointerCount == 1) { if (widget.isLongPic) { final imageHeight = _scale * _imageSize.height; @@ -352,7 +353,7 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { _positionFrom = _position; _positionTo = position; - _effectiveAnimationController + _animationController ..duration = Duration(milliseconds: (tFinal * 1000).round()) ..forward(from: 0); case _GestureType.scale: @@ -376,7 +377,7 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { // end: frictionSimulation.x(tFinal), // ).chain(CurveTween(curve: Curves.decelerate)), // )..addListener(_handleScaleAnimation); - // _effectiveAnimationController + // _animationController // ..duration = Duration(milliseconds: (tFinal * 1000).round()) // ..forward(from: 0); break; @@ -407,7 +408,10 @@ class _ViewerState extends State with SingleTickerProviderStateMixin { void _onPointerDown(PointerDownEvent event) { _scalePos = event.position; _tapGestureRecognizer.addPointer(event); - _doubleTapGestureRecognizer.addPointer(event); + _doubleTapGestureRecognizer + ..onDoubleTapDown = _onDoubleTapDown + ..onDoubleTap = _onDoubleTap + ..addPointer(event); _horizontalDragGestureRecognizer ..isBoundaryAllowed = _isBoundaryAllowed ..addPointer(event);