From 5271863291c1c75ededb2eca28b52faf769447d9 Mon Sep 17 00:00:00 2001 From: LeLunZ <31982496+LeLunZ@users.noreply.github.com> Date: Sat, 25 Apr 2026 16:36:50 +0200 Subject: [PATCH] fix(mobile): prevent live photo from getting stuck during dismiss animation --- .../asset_viewer/asset_page.widget.dart | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_page.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_page.widget.dart index 0934536471..be91dca313 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_page.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_page.widget.dart @@ -48,6 +48,9 @@ class _AssetPageState extends ConsumerState { bool _showingDetails = false; bool _isZoomed = false; + // Frozen during dismiss drag + settle to prevent widget tree swap mid-animation. + bool _frozenMotionPlaying = false; + bool _dismissSettling = false; final _scrollController = SnapScrollController(); double _snapOffset = 0.0; @@ -135,6 +138,9 @@ class _AssetPageState extends ConsumerState { > 0 => _DragIntent.dismiss, _ => _DragIntent.none, }; + if (_dragIntent == _DragIntent.dismiss) { + _frozenMotionPlaying = ref.read(isPlayingMotionVideoProvider); + } } switch (_dragIntent) { @@ -172,12 +178,18 @@ class _AssetPageState extends ConsumerState { context.maybePop(); return; } - _viewController?.animateMultiple( - position: _initialPhotoViewState.position, - scale: _viewController?.initialScale ?? _initialPhotoViewState.scale, - rotation: _initialPhotoViewState.rotation, - ); _viewer.setOpacity(1.0); + _dismissSettling = true; + _viewController + ?.animateMultiple( + position: _initialPhotoViewState.position, + scale: _viewController?.initialScale ?? _initialPhotoViewState.scale, + rotation: _initialPhotoViewState.rotation, + ) + .whenComplete(() { + if (!mounted) return; + setState(() => _dismissSettling = false); + }); } } @@ -355,7 +367,10 @@ class _AssetPageState extends ConsumerState { final currentHeroTag = ref.watch(assetViewerProvider.select((s) => s.currentAsset?.heroTag)); _showingDetails = ref.watch(assetViewerProvider.select((s) => s.showingDetails)); final stackIndex = ref.watch(assetViewerProvider.select((s) => s.stackIndex)); - final isPlayingMotionVideo = ref.watch(isPlayingMotionVideoProvider); + final liveMotionPlaying = ref.watch(isPlayingMotionVideoProvider); + final isPlayingMotionVideo = (_dragIntent == _DragIntent.dismiss || _dismissSettling) + ? _frozenMotionPlaying + : liveMotionPlaying; final asset = ref.read(timelineServiceProvider).getAssetSafe(widget.index); if (asset == null) {