Reenable hero using the thumbnail

This commit is contained in:
Ming Ming 2022-12-18 19:52:57 +08:00
parent 9ab9f11fb5
commit 1fc9de5af2
3 changed files with 130 additions and 24 deletions

View file

@ -19,3 +19,44 @@ class CustomizableMaterialPageRoute extends MaterialPageRoute {
} }
String getImageHeroTag(FileDescriptor file) => "imageHero(${file.fdPath})"; String getImageHeroTag(FileDescriptor file) => "imageHero(${file.fdPath})";
// copied from flutter
Widget defaultHeroFlightShuttleBuilder(
BuildContext flightContext,
Animation<double> animation,
HeroFlightDirection flightDirection,
BuildContext fromHeroContext,
BuildContext toHeroContext,
) {
final Hero toHero = toHeroContext.widget as Hero;
final MediaQueryData? toMediaQueryData = MediaQuery.maybeOf(toHeroContext);
final MediaQueryData? fromMediaQueryData =
MediaQuery.maybeOf(fromHeroContext);
if (toMediaQueryData == null || fromMediaQueryData == null) {
return toHero.child;
}
final EdgeInsets fromHeroPadding = fromMediaQueryData.padding;
final EdgeInsets toHeroPadding = toMediaQueryData.padding;
return AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget? child) {
return MediaQuery(
data: toMediaQueryData.copyWith(
padding: (flightDirection == HeroFlightDirection.push)
? EdgeInsetsTween(
begin: fromHeroPadding,
end: toHeroPadding,
).evaluate(animation)
: EdgeInsetsTween(
begin: toHeroPadding,
end: fromHeroPadding,
).evaluate(animation),
),
child: toHero.child);
},
);
}

View file

@ -1,3 +1,4 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart'; import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@ -8,9 +9,11 @@ import 'package:nc_photos/api/api_util.dart' as api_util;
import 'package:nc_photos/cache_manager_util.dart'; import 'package:nc_photos/cache_manager_util.dart';
import 'package:nc_photos/entity/file_descriptor.dart'; import 'package:nc_photos/entity/file_descriptor.dart';
import 'package:nc_photos/entity/local_file.dart'; import 'package:nc_photos/entity/local_file.dart';
import 'package:nc_photos/flutter_util.dart' as flutter_util;
import 'package:nc_photos/k.dart' as k; import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/mobile/android/content_uri_image_provider.dart'; import 'package:nc_photos/mobile/android/content_uri_image_provider.dart';
import 'package:nc_photos/widget/cached_network_image_mod.dart' as mod; import 'package:nc_photos/widget/cached_network_image_mod.dart' as mod;
import 'package:nc_photos/widget/network_thumbnail.dart';
import 'package:np_codegen/np_codegen.dart'; import 'package:np_codegen/np_codegen.dart';
part 'image_viewer.g.dart'; part 'image_viewer.g.dart';
@ -113,30 +116,78 @@ class RemoteImageViewer extends StatefulWidget {
@npLog @npLog
class _RemoteImageViewerState extends State<RemoteImageViewer> { class _RemoteImageViewerState extends State<RemoteImageViewer> {
@override @override
build(BuildContext context) => _ImageViewer( void initState() {
canZoom: widget.canZoom, super.initState();
onHeightChanged: widget.onHeightChanged, WidgetsBinding.instance.addPostFrameCallback((_) {
onZoomStarted: widget.onZoomStarted, if (mounted) {
onZoomEnded: widget.onZoomEnded, // needed to get rid of the large image blinking during Hero animation
child: mod.CachedNetworkImage( setState(() {});
cacheManager: LargeImageCacheManager.inst, }
imageUrl: _getImageUrl(widget.account, widget.file), });
httpHeaders: { }
"Authorization": Api.getAuthorizationHeaderValue(widget.account),
}, @override
fit: BoxFit.contain, Widget build(BuildContext context) {
fadeInDuration: const Duration(), return _ImageViewer(
filterQuality: FilterQuality.high, canZoom: widget.canZoom,
imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet, onHeightChanged: widget.onHeightChanged,
imageBuilder: (context, child, imageProvider) { onZoomStarted: widget.onZoomStarted,
WidgetsBinding.instance.addPostFrameCallback((_) { onZoomEnded: widget.onZoomEnded,
_onItemLoaded(); child: Stack(
}); fit: StackFit.passthrough,
const SizeChangedLayoutNotification().dispatch(context); children: [
return child; Hero(
}, tag: flutter_util.getImageHeroTag(widget.file),
), flightShuttleBuilder: (flightContext, animation, flightDirection,
); fromHeroContext, toHeroContext) {
_isHeroDone = false;
animation.addStatusListener(_animationListener);
return flutter_util.defaultHeroFlightShuttleBuilder(
flightContext,
animation,
flightDirection,
fromHeroContext,
toHeroContext,
);
},
child: CachedNetworkImage(
fit: BoxFit.contain,
cacheManager: ThumbnailCacheManager.inst,
imageUrl: NetworkRectThumbnail.imageUrlForFile(
widget.account, widget.file),
httpHeaders: {
"Authorization":
Api.getAuthorizationHeaderValue(widget.account),
},
fadeInDuration: const Duration(),
filterQuality: FilterQuality.high,
imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet,
),
),
if (_isHeroDone)
mod.CachedNetworkImage(
cacheManager: LargeImageCacheManager.inst,
imageUrl: _getImageUrl(widget.account, widget.file),
httpHeaders: {
"Authorization":
Api.getAuthorizationHeaderValue(widget.account),
},
fit: BoxFit.contain,
fadeInDuration: const Duration(),
filterQuality: FilterQuality.high,
imageRenderMethodForWeb: ImageRenderMethodForWeb.HttpGet,
imageBuilder: (context, child, imageProvider) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_onItemLoaded();
});
const SizeChangedLayoutNotification().dispatch(context);
return child;
},
),
],
),
);
}
void _onItemLoaded() { void _onItemLoaded() {
if (!_isLoaded) { if (!_isLoaded) {
@ -146,7 +197,19 @@ class _RemoteImageViewerState extends State<RemoteImageViewer> {
} }
} }
void _animationListener(AnimationStatus status) {
if (status == AnimationStatus.completed) {
_isHeroDone = true;
if (mounted) {
setState(() {});
}
}
}
var _isLoaded = false; var _isLoaded = false;
// initially set to true such that the large image will show when hero didn't
// run (i.e., when swiping in viewer)
var _isHeroDone = true;
} }
class _ImageViewer extends StatefulWidget { class _ImageViewer extends StatefulWidget {

View file

@ -6,6 +6,7 @@ import 'package:nc_photos/account.dart';
import 'package:nc_photos/app_localizations.dart'; import 'package:nc_photos/app_localizations.dart';
import 'package:nc_photos/entity/file_descriptor.dart'; import 'package:nc_photos/entity/file_descriptor.dart';
import 'package:nc_photos/entity/local_file.dart'; import 'package:nc_photos/entity/local_file.dart';
import 'package:nc_photos/flutter_util.dart' as flutter_util;
import 'package:nc_photos/k.dart' as k; import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/mobile/android/content_uri_image_provider.dart'; import 'package:nc_photos/mobile/android/content_uri_image_provider.dart';
import 'package:nc_photos/theme.dart'; import 'package:nc_photos/theme.dart';
@ -63,6 +64,7 @@ class PhotoListImageItem extends PhotoListFileItem {
previewUrl: previewUrl, previewUrl: previewUrl,
isGif: file.fdMime == "image/gif", isGif: file.fdMime == "image/gif",
isFavorite: shouldShowFavoriteBadge && file.fdIsFavorite == true, isFavorite: shouldShowFavoriteBadge && file.fdIsFavorite == true,
heroKey: flutter_util.getImageHeroTag(file),
); );
final Account account; final Account account;