From 09cef69d106a9ae8a35c24f3874bce956671a152 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Fri, 3 Apr 2020 10:59:16 +0900 Subject: [PATCH] use cached image during hero animation if possible --- lib/widgets/album/thumbnail.dart | 17 ++++++++++++++--- lib/widgets/common/transition_image.dart | 2 ++ lib/widgets/fullscreen/image_view.dart | 21 ++++++++++++++------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/lib/widgets/album/thumbnail.dart b/lib/widgets/album/thumbnail.dart index dfe44f529..de30930ba 100644 --- a/lib/widgets/album/thumbnail.dart +++ b/lib/widgets/album/thumbnail.dart @@ -4,6 +4,7 @@ import 'package:aves/model/image_entry.dart'; import 'package:aves/utils/constants.dart'; import 'package:aves/widgets/common/icons.dart'; import 'package:aves/widgets/common/image_providers/thumbnail_provider.dart'; +import 'package:aves/widgets/common/image_providers/uri_image_provider.dart'; import 'package:aves/widgets/common/image_providers/uri_picture_provider.dart'; import 'package:aves/widgets/common/transition_image.dart'; import 'package:flutter/material.dart'; @@ -49,9 +50,9 @@ class Thumbnail extends StatelessWidget { } Widget _buildRasterImage() { - final provider = ThumbnailProvider(entry: entry, extent: Constants.thumbnailCacheExtent); + final thumbnailProvider = ThumbnailProvider(entry: entry, extent: Constants.thumbnailCacheExtent); final image = Image( - image: provider, + image: thumbnailProvider, width: extent, height: extent, fit: BoxFit.cover, @@ -61,8 +62,18 @@ class Thumbnail extends StatelessWidget { : Hero( tag: heroTag, flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) { + ImageProvider heroImageProvider = thumbnailProvider; + if (!entry.isVideo && !entry.isSvg) { + final imageProvider = UriImage( + uri: entry.uri, + mimeType: entry.mimeType, + ); + if (imageCache.statusForKey(imageProvider).keepAlive) { + heroImageProvider = imageProvider; + } + } return TransitionImage( - image: provider, + image: heroImageProvider, animation: animation, ); }, diff --git a/lib/widgets/common/transition_image.dart b/lib/widgets/common/transition_image.dart index 000c0bf26..3eb9ad165 100644 --- a/lib/widgets/common/transition_image.dart +++ b/lib/widgets/common/transition_image.dart @@ -159,6 +159,8 @@ class _TransitionImagePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { + if (image == null) return; + final paint = Paint() ..isAntiAlias = false ..filterQuality = FilterQuality.low; diff --git a/lib/widgets/fullscreen/image_view.dart b/lib/widgets/fullscreen/image_view.dart index 55bbfd558..c3be77fe6 100644 --- a/lib/widgets/fullscreen/image_view.dart +++ b/lib/widgets/fullscreen/image_view.dart @@ -54,7 +54,7 @@ class ImageView extends StatelessWidget { // if the hero tag wraps the whole `PhotoView` and the `loadingBuilder` is not provided, // there's a black frame between the hero animation and the final image, even when it's cached. - final loadingBuilder = (context) => Center( + final thumbnailLoadingBuilder = (context) => Center( child: AspectRatio( // enforce original aspect ratio, as some thumbnails aspect ratios slightly differ aspectRatio: entry.displayAspectRatio, @@ -77,7 +77,7 @@ class ImageView extends StatelessWidget { mimeType: entry.mimeType, colorFilter: Constants.svgColorFilter, ), - placeholderBuilder: loadingBuilder, + placeholderBuilder: thumbnailLoadingBuilder, ), backgroundDecoration: backgroundDecoration, scaleStateChangedCallback: onScaleChanged, @@ -86,14 +86,21 @@ class ImageView extends StatelessWidget { onTapUp: (tapContext, details, value) => onTap?.call(), ); } else { + final uriImage = UriImage( + uri: entry.uri, + mimeType: entry.mimeType, + ); child = PhotoView( // key includes size and orientation to refresh when the image is rotated key: ValueKey('${entry.orientationDegrees}_${entry.width}_${entry.height}_${entry.path}'), - imageProvider: UriImage( - uri: entry.uri, - mimeType: entry.mimeType, - ), - loadingBuilder: (context, event) => loadingBuilder(context), + imageProvider: uriImage, + // when the full image is ready, we use it in the `loadingBuilder` + // we still provide a `loadingBuilder` in that case to avoid a black frame after hero animation + loadingBuilder: (context, event) => imageCache.statusForKey(uriImage).keepAlive + ? Image( + image: uriImage, + ) + : thumbnailLoadingBuilder(context), backgroundDecoration: backgroundDecoration, scaleStateChangedCallback: onScaleChanged, minScale: PhotoViewComputedScale.contained,