From 7905853639916b4d74937a33790ee4457ff30308 Mon Sep 17 00:00:00 2001 From: Luis Nachtigall <31982496+LeLunZ@users.noreply.github.com> Date: Tue, 26 May 2026 17:40:02 +0200 Subject: [PATCH] fix(mobile): preserve zoom level during image loading and live photo playback (#27960) * fix(mobile): preserve zoom level when new images load in asset viewer * fix(mobile): use actual child size for live photo * revert fixes * fix(mobile): keep zoom consistent when scale boundaries change * fix(mobile): simplify scale handling in photo_view_core.dart --- .../photo_view/src/core/photo_view_core.dart | 29 +++++++++++++------ .../photo_view/src/photo_view_wrappers.dart | 1 - 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart index 2f775f57e2..5009ee5333 100644 --- a/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart +++ b/mobile/lib/widgets/photo_view/src/core/photo_view_core.dart @@ -139,8 +139,6 @@ class PhotoViewCoreState extends State PhotoViewHeroAttributes? get heroAttributes => widget.heroAttributes; - late ScaleBoundaries cachedScaleBoundaries = widget.scaleBoundaries; - void handleScaleAnimation() { scale = _scaleAnimation!.value; } @@ -303,7 +301,7 @@ class PhotoViewCoreState extends State controller.scaleAnimationBuilder(_animateControllerScale); controller.rotationAnimationBuilder(_animateControllerRotation); - cachedScaleBoundaries = widget.scaleBoundaries; + _updateScaleBoundaries(); _scaleAnimationController = AnimationController(vsync: this) ..addListener(handleScaleAnimation) @@ -334,14 +332,27 @@ class PhotoViewCoreState extends State widget.onTapDown?.call(context, details, controller.value); } + void _updateScaleBoundaries() { + final prev = controller.scaleBoundaries; + if (prev == widget.scaleBoundaries) return; + + if (prev != null && controller.scale != null && prev.initialScale > 0) { + final ratio = widget.scaleBoundaries.initialScale / prev.initialScale; + controller.setScaleInvisibly(controller.scale! * ratio); + } else { + markNeedsScaleRecalc = true; + } + controller.scaleBoundaries = widget.scaleBoundaries; + } + + @override + void didUpdateWidget(PhotoViewCore oldWidget) { + super.didUpdateWidget(oldWidget); + _updateScaleBoundaries(); + } + @override Widget build(BuildContext context) { - // Check if we need a recalc on the scale - if (widget.scaleBoundaries != cachedScaleBoundaries) { - markNeedsScaleRecalc = true; - cachedScaleBoundaries = widget.scaleBoundaries; - } - return StreamBuilder( stream: controller.outputStateStream, initialData: controller.prevValue, diff --git a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart index d8d2ae7ee5..a9cfeb3a40 100644 --- a/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart +++ b/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart @@ -145,7 +145,6 @@ class _ImageWrapperState extends State { _lastStack = null; _didLoadSynchronously = synchronousCall; - widget.controller.scaleBoundaries = scaleBoundaries; } synchronousCall && !_didLoadSynchronously ? setupCB() : setState(setupCB);