use cached image during hero animation if possible

This commit is contained in:
Thibault Deckers 2020-04-03 10:59:16 +09:00
parent 142b4a5ade
commit 09cef69d10
3 changed files with 30 additions and 10 deletions

View file

@ -4,6 +4,7 @@ import 'package:aves/model/image_entry.dart';
import 'package:aves/utils/constants.dart'; import 'package:aves/utils/constants.dart';
import 'package:aves/widgets/common/icons.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/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/image_providers/uri_picture_provider.dart';
import 'package:aves/widgets/common/transition_image.dart'; import 'package:aves/widgets/common/transition_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -49,9 +50,9 @@ class Thumbnail extends StatelessWidget {
} }
Widget _buildRasterImage() { Widget _buildRasterImage() {
final provider = ThumbnailProvider(entry: entry, extent: Constants.thumbnailCacheExtent); final thumbnailProvider = ThumbnailProvider(entry: entry, extent: Constants.thumbnailCacheExtent);
final image = Image( final image = Image(
image: provider, image: thumbnailProvider,
width: extent, width: extent,
height: extent, height: extent,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -61,8 +62,18 @@ class Thumbnail extends StatelessWidget {
: Hero( : Hero(
tag: heroTag, tag: heroTag,
flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) { 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( return TransitionImage(
image: provider, image: heroImageProvider,
animation: animation, animation: animation,
); );
}, },

View file

@ -159,6 +159,8 @@ class _TransitionImagePainter extends CustomPainter {
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
if (image == null) return;
final paint = Paint() final paint = Paint()
..isAntiAlias = false ..isAntiAlias = false
..filterQuality = FilterQuality.low; ..filterQuality = FilterQuality.low;

View file

@ -54,7 +54,7 @@ class ImageView extends StatelessWidget {
// if the hero tag wraps the whole `PhotoView` and the `loadingBuilder` is not provided, // 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. // 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( child: AspectRatio(
// enforce original aspect ratio, as some thumbnails aspect ratios slightly differ // enforce original aspect ratio, as some thumbnails aspect ratios slightly differ
aspectRatio: entry.displayAspectRatio, aspectRatio: entry.displayAspectRatio,
@ -77,7 +77,7 @@ class ImageView extends StatelessWidget {
mimeType: entry.mimeType, mimeType: entry.mimeType,
colorFilter: Constants.svgColorFilter, colorFilter: Constants.svgColorFilter,
), ),
placeholderBuilder: loadingBuilder, placeholderBuilder: thumbnailLoadingBuilder,
), ),
backgroundDecoration: backgroundDecoration, backgroundDecoration: backgroundDecoration,
scaleStateChangedCallback: onScaleChanged, scaleStateChangedCallback: onScaleChanged,
@ -86,14 +86,21 @@ class ImageView extends StatelessWidget {
onTapUp: (tapContext, details, value) => onTap?.call(), onTapUp: (tapContext, details, value) => onTap?.call(),
); );
} else { } else {
final uriImage = UriImage(
uri: entry.uri,
mimeType: entry.mimeType,
);
child = PhotoView( child = PhotoView(
// key includes size and orientation to refresh when the image is rotated // key includes size and orientation to refresh when the image is rotated
key: ValueKey('${entry.orientationDegrees}_${entry.width}_${entry.height}_${entry.path}'), key: ValueKey('${entry.orientationDegrees}_${entry.width}_${entry.height}_${entry.path}'),
imageProvider: UriImage( imageProvider: uriImage,
uri: entry.uri, // when the full image is ready, we use it in the `loadingBuilder`
mimeType: entry.mimeType, // we still provide a `loadingBuilder` in that case to avoid a black frame after hero animation
), loadingBuilder: (context, event) => imageCache.statusForKey(uriImage).keepAlive
loadingBuilder: (context, event) => loadingBuilder(context), ? Image(
image: uriImage,
)
: thumbnailLoadingBuilder(context),
backgroundDecoration: backgroundDecoration, backgroundDecoration: backgroundDecoration,
scaleStateChangedCallback: onScaleChanged, scaleStateChangedCallback: onScaleChanged,
minScale: PhotoViewComputedScale.contained, minScale: PhotoViewComputedScale.contained,