Merge pull request #815 from ente-io/fix_zoom

Set the zoom-scale correctly once full-res image has loaded
This commit is contained in:
Vishnu Mohandas 2023-01-18 12:45:05 +05:30 committed by GitHub
commit 46ec158a18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 3 deletions

View file

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
@ -13,6 +14,7 @@ import 'package:photos/events/local_photos_updated_event.dart';
import 'package:photos/models/file.dart';
import 'package:photos/ui/common/loading_widget.dart';
import 'package:photos/utils/file_util.dart';
import 'package:photos/utils/image_util.dart';
import 'package:photos/utils/thumbnail_util.dart';
class ZoomableImage extends StatefulWidget {
@ -35,7 +37,7 @@ class ZoomableImage extends StatefulWidget {
class _ZoomableImageState extends State<ZoomableImage>
with SingleTickerProviderStateMixin {
final Logger _logger = Logger("ZoomableImage");
late Logger _logger;
late File _photo;
ImageProvider? _imageProvider;
bool _loadedSmallThumbnail = false;
@ -45,10 +47,13 @@ class _ZoomableImageState extends State<ZoomableImage>
bool _loadedFinalImage = false;
ValueChanged<PhotoViewScaleState>? _scaleStateChangedCallback;
bool _isZooming = false;
PhotoViewController _photoViewController = PhotoViewController();
int? _thumbnailWidth;
@override
void initState() {
_photo = widget.photo;
_logger = Logger("ZoomableImage_" + _photo.displayName);
debugPrint('initState for ${_photo.toString()}');
_scaleStateChangedCallback = (value) {
if (widget.shouldDisableScroll != null) {
@ -61,6 +66,12 @@ class _ZoomableImageState extends State<ZoomableImage>
super.initState();
}
@override
void dispose() {
_photoViewController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_photo.isRemoteFile) {
@ -75,6 +86,7 @@ class _ZoomableImageState extends State<ZoomableImage>
axis: Axis.vertical,
child: PhotoView(
imageProvider: _imageProvider,
controller: _photoViewController,
scaleStateChangedCallback: _scaleStateChangedCallback,
minScale: PhotoViewComputedScale.contained,
gaplessPlayback: true,
@ -106,6 +118,7 @@ class _ZoomableImageState extends State<ZoomableImage>
if (cachedThumbnail != null) {
_imageProvider = Image.memory(cachedThumbnail).image;
_loadedSmallThumbnail = true;
_captureThumbnailDimensions(_imageProvider!);
} else {
getThumbnailFromServer(_photo).then((file) {
final imageProvider = Image.memory(file).image;
@ -115,6 +128,7 @@ class _ZoomableImageState extends State<ZoomableImage>
setState(() {
_imageProvider = imageProvider;
_loadedSmallThumbnail = true;
_captureThumbnailDimensions(_imageProvider!);
});
}
}).catchError((e) {
@ -125,7 +139,8 @@ class _ZoomableImageState extends State<ZoomableImage>
});
}
}
if (!_loadedFinalImage) {
if (!_loadedFinalImage && !_loadingFinalImage) {
_loadingFinalImage = true;
getFileFromServer(_photo).then((file) {
_onFinalImageLoaded(
Image.file(
@ -209,16 +224,42 @@ class _ZoomableImageState extends State<ZoomableImage>
void _onFinalImageLoaded(ImageProvider imageProvider) {
if (mounted) {
precacheImage(imageProvider, context).then((value) {
precacheImage(imageProvider, context).then((value) async {
if (mounted) {
await _updatePhotoViewController(imageProvider);
setState(() {
_imageProvider = imageProvider;
_loadedFinalImage = true;
_logger.info("Final image loaded");
});
}
});
}
}
Future<void> _captureThumbnailDimensions(ImageProvider imageProvider) async {
final imageInfo = await getImageInfo(imageProvider);
_thumbnailWidth = imageInfo.image.width;
}
Future<void> _updatePhotoViewController(ImageProvider imageProvider) async {
if (_thumbnailWidth == null || _photoViewController.scale == null) {
return;
}
final imageInfo = await getImageInfo(imageProvider);
final scale = _photoViewController.scale! /
(imageInfo.image.width / _thumbnailWidth!);
final currentPosition = _photoViewController.value.position;
final positionScaleFactor = 1 / scale;
final newPosition = currentPosition.scale(
positionScaleFactor,
positionScaleFactor,
);
_photoViewController = PhotoViewController(
initialPosition: newPosition,
initialScale: scale,
);
}
bool _isGIF() => _photo.displayName.toLowerCase().endsWith(".gif");
}

16
lib/utils/image_util.dart Normal file
View file

@ -0,0 +1,16 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
Future<ImageInfo> getImageInfo(ImageProvider imageProvider) {
final completer = Completer<ImageInfo>();
final imageStream = imageProvider.resolve(const ImageConfiguration());
final listener = ImageStreamListener(
((imageInfo, _) {
completer.complete(imageInfo);
}),
);
imageStream.addListener(listener);
completer.future.whenComplete(() => imageStream.removeListener(listener));
return completer.future;
}