mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
fix(mobile): asset viewer hero animation (#26545)
The image in the photo view has no height, and is therefore entirely unconstrained. This causes the image to take up the full height of the viewport during the hero animation, which can make look out of sync. In some other cases, it can stretch or resize the image to fill the entire viewport.
This commit is contained in:
@@ -14,7 +14,6 @@ import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_details.wi
|
|||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_stack.provider.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart';
|
|
||||||
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
@@ -329,40 +328,40 @@ class _AssetPageState extends ConsumerState<AssetPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Stack(
|
final Size childSize;
|
||||||
children: [
|
if (displayAsset.width != null && displayAsset.height != null) {
|
||||||
PhotoView.customChild(
|
final r = displayAsset.width! / displayAsset.height!;
|
||||||
key: Key(displayAsset.heroTag),
|
final w = math.min(context.width, context.height * r);
|
||||||
onDragStart: _onDragStart,
|
childSize = Size(w, w / r);
|
||||||
onDragUpdate: _onDragUpdate,
|
} else {
|
||||||
onDragEnd: _onDragEnd,
|
childSize = Size(context.height, context.height);
|
||||||
onDragCancel: _onDragCancel,
|
}
|
||||||
onTapUp: _onTapUp,
|
|
||||||
heroAttributes: heroAttributes,
|
return PhotoView.customChild(
|
||||||
basePosition: Alignment.center,
|
key: Key(displayAsset.heroTag),
|
||||||
disableScaleGestures: showingDetails,
|
childSize: childSize,
|
||||||
scaleStateChangedCallback: _onScaleStateChanged,
|
filterQuality: FilterQuality.low,
|
||||||
onPageBuild: _onPageBuild,
|
onDragStart: _onDragStart,
|
||||||
enablePanAlways: true,
|
onDragUpdate: _onDragUpdate,
|
||||||
backgroundDecoration: backgroundDecoration,
|
onDragEnd: _onDragEnd,
|
||||||
child: SizedBox(
|
onDragCancel: _onDragCancel,
|
||||||
width: context.width,
|
onTapUp: _onTapUp,
|
||||||
height: context.height,
|
heroAttributes: heroAttributes,
|
||||||
child: NativeVideoViewer(
|
basePosition: Alignment.center,
|
||||||
key: _NativeVideoViewerKey(displayAsset.heroTag),
|
disableScaleGestures: showingDetails,
|
||||||
asset: displayAsset,
|
scaleStateChangedCallback: _onScaleStateChanged,
|
||||||
image: Image(
|
onPageBuild: _onPageBuild,
|
||||||
image: getFullImageProvider(displayAsset, size: context.sizeData),
|
enablePanAlways: true,
|
||||||
fit: BoxFit.contain,
|
backgroundDecoration: backgroundDecoration,
|
||||||
height: context.height,
|
child: NativeVideoViewer(
|
||||||
width: context.width,
|
key: _NativeVideoViewerKey(displayAsset.heroTag),
|
||||||
alignment: Alignment.center,
|
asset: displayAsset,
|
||||||
),
|
image: Image(
|
||||||
),
|
image: getFullImageProvider(displayAsset, size: childSize),
|
||||||
),
|
fit: BoxFit.contain,
|
||||||
|
alignment: Alignment.center,
|
||||||
),
|
),
|
||||||
const Center(child: VideoViewerControls()),
|
),
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import 'package:immich_mobile/domain/services/setting.service.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||||
@@ -393,13 +394,9 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||||||
if (aspectRatio.value != null && !isCasting)
|
if (aspectRatio.value != null && !isCasting)
|
||||||
Visibility.maintain(
|
Visibility.maintain(
|
||||||
visible: isVisible.value,
|
visible: isVisible.value,
|
||||||
child: Center(
|
child: NativeVideoPlayerView(onViewReady: initController),
|
||||||
child: AspectRatio(
|
|
||||||
aspectRatio: aspectRatio.value!,
|
|
||||||
child: isCurrent ? NativeVideoPlayerView(onViewReady: initController) : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
const Center(child: VideoViewerControls()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -420,7 +420,11 @@ class PhotoViewCoreState extends State<PhotoViewCore>
|
|||||||
|
|
||||||
Widget _buildChild() {
|
Widget _buildChild() {
|
||||||
return widget.hasCustomChild
|
return widget.hasCustomChild
|
||||||
? widget.customChild!
|
? SizedBox(
|
||||||
|
width: scaleBoundaries.childSize.width * scale,
|
||||||
|
height: scaleBoundaries.childSize.height * scale,
|
||||||
|
child: widget.customChild!,
|
||||||
|
)
|
||||||
: Image(
|
: Image(
|
||||||
key: widget.heroAttributes?.tag != null ? ObjectKey(widget.heroAttributes!.tag) : null,
|
key: widget.heroAttributes?.tag != null ? ObjectKey(widget.heroAttributes!.tag) : null,
|
||||||
image: widget.imageProvider!,
|
image: widget.imageProvider!,
|
||||||
@@ -428,7 +432,7 @@ class PhotoViewCoreState extends State<PhotoViewCore>
|
|||||||
gaplessPlayback: widget.gaplessPlayback ?? false,
|
gaplessPlayback: widget.gaplessPlayback ?? false,
|
||||||
filterQuality: widget.filterQuality,
|
filterQuality: widget.filterQuality,
|
||||||
width: scaleBoundaries.childSize.width * scale,
|
width: scaleBoundaries.childSize.width * scale,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.contain,
|
||||||
isAntiAlias: widget.filterQuality == FilterQuality.high,
|
isAntiAlias: widget.filterQuality == FilterQuality.high,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user