zoomable_image.dart 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import 'package:cached_network_image/cached_network_image.dart';
  2. import 'package:flutter/foundation.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter/widgets.dart';
  5. import 'package:logging/logging.dart';
  6. import 'package:photos/core/cache/image_cache.dart';
  7. import 'package:photos/core/cache/thumbnail_cache.dart';
  8. import 'package:photos/models/file.dart';
  9. import 'package:photos/ui/loading_widget.dart';
  10. import 'package:photo_view/photo_view.dart';
  11. import 'package:photos/core/constants.dart';
  12. import 'package:photos/utils/file_util.dart';
  13. class ZoomableImage extends StatefulWidget {
  14. final File photo;
  15. final Function(bool) shouldDisableScroll;
  16. final String tagPrefix;
  17. final Decoration backgroundDecoration;
  18. ZoomableImage(
  19. this.photo, {
  20. Key key,
  21. this.shouldDisableScroll,
  22. @required this.tagPrefix,
  23. this.backgroundDecoration,
  24. }) : super(key: key);
  25. @override
  26. _ZoomableImageState createState() => _ZoomableImageState();
  27. }
  28. class _ZoomableImageState extends State<ZoomableImage>
  29. with SingleTickerProviderStateMixin {
  30. final Logger _logger = Logger("ZoomableImage");
  31. File _photo;
  32. ImageProvider _imageProvider;
  33. bool _loadedSmallThumbnail = false;
  34. bool _loadingLargeThumbnail = false;
  35. bool _loadedLargeThumbnail = false;
  36. bool _loadingFinalImage = false;
  37. bool _loadedFinalImage = false;
  38. ValueChanged<PhotoViewScaleState> _scaleStateChangedCallback;
  39. @override
  40. void initState() {
  41. _scaleStateChangedCallback = (value) {
  42. if (widget.shouldDisableScroll != null) {
  43. widget.shouldDisableScroll(value != PhotoViewScaleState.initial);
  44. }
  45. };
  46. super.initState();
  47. }
  48. @override
  49. Widget build(BuildContext context) {
  50. _photo = widget.photo;
  51. if (_photo.localID == null) {
  52. _loadNetworkImage();
  53. } else {
  54. _loadLocalImage(context);
  55. }
  56. if (_imageProvider != null) {
  57. return PhotoView(
  58. imageProvider: _imageProvider,
  59. scaleStateChangedCallback: _scaleStateChangedCallback,
  60. minScale: PhotoViewComputedScale.contained,
  61. gaplessPlayback: true,
  62. heroAttributes: PhotoViewHeroAttributes(
  63. tag: widget.tagPrefix + _photo.tag(),
  64. ),
  65. backgroundDecoration: widget.backgroundDecoration,
  66. );
  67. } else {
  68. return loadWidget;
  69. }
  70. }
  71. void _loadNetworkImage() {
  72. if (!_photo.isEncrypted) {
  73. _loadUnencryptedThumbnail();
  74. } else {
  75. _loadEncryptedThumbnail();
  76. }
  77. if (!_loadedFinalImage) {
  78. getFileFromServer(_photo).then((file) {
  79. _onFinalImageLoaded(
  80. Image.file(
  81. file,
  82. gaplessPlayback: true,
  83. ).image,
  84. context);
  85. });
  86. }
  87. }
  88. void _loadUnencryptedThumbnail() {
  89. if (!_loadedSmallThumbnail && !_loadedFinalImage) {
  90. _imageProvider = CachedNetworkImageProvider(_photo.getThumbnailUrl());
  91. _loadedSmallThumbnail = true;
  92. }
  93. }
  94. void _loadEncryptedThumbnail() {
  95. if (!_loadedSmallThumbnail && !_loadedFinalImage) {
  96. if (ThumbnailFileLruCache.get(_photo) != null) {
  97. _imageProvider = Image.file(
  98. ThumbnailFileLruCache.get(_photo),
  99. ).image;
  100. _loadedSmallThumbnail = true;
  101. }
  102. }
  103. }
  104. void _loadLocalImage(BuildContext context) {
  105. if (!_loadedSmallThumbnail &&
  106. !_loadedLargeThumbnail &&
  107. !_loadedFinalImage) {
  108. final cachedThumbnail =
  109. ThumbnailLruCache.get(_photo, THUMBNAIL_SMALL_SIZE);
  110. if (cachedThumbnail != null) {
  111. _imageProvider = Image.memory(cachedThumbnail).image;
  112. _loadedSmallThumbnail = true;
  113. }
  114. }
  115. if (!_loadingLargeThumbnail &&
  116. !_loadedLargeThumbnail &&
  117. !_loadedFinalImage) {
  118. _loadingLargeThumbnail = true;
  119. final cachedThumbnail =
  120. ThumbnailLruCache.get(_photo, THUMBNAIL_LARGE_SIZE);
  121. if (cachedThumbnail != null) {
  122. _onLargeThumbnailLoaded(Image.memory(cachedThumbnail).image, context);
  123. } else {
  124. _photo.getAsset().then((asset) {
  125. asset
  126. .thumbDataWithSize(THUMBNAIL_LARGE_SIZE, THUMBNAIL_LARGE_SIZE)
  127. .then((data) {
  128. _onLargeThumbnailLoaded(Image.memory(data).image, context);
  129. ThumbnailLruCache.put(_photo, THUMBNAIL_LARGE_SIZE, data);
  130. });
  131. });
  132. }
  133. }
  134. if (!_loadingFinalImage && !_loadedFinalImage) {
  135. _loadingFinalImage = true;
  136. final cachedFile = FileLruCache.get(_photo);
  137. if (cachedFile != null) {
  138. _onFinalImageLoaded(Image.file(cachedFile).image, context);
  139. } else {
  140. _photo.getAsset().then((asset) {
  141. asset.file.then((file) {
  142. if (mounted) {
  143. _onFinalImageLoaded(Image.file(file).image, context);
  144. FileLruCache.put(_photo, file);
  145. }
  146. });
  147. });
  148. }
  149. }
  150. }
  151. void _onLargeThumbnailLoaded(
  152. ImageProvider imageProvider, BuildContext context) {
  153. if (mounted && !_loadedFinalImage) {
  154. precacheImage(imageProvider, context).then((value) {
  155. if (mounted && !_loadedFinalImage) {
  156. setState(() {
  157. _imageProvider = imageProvider;
  158. _loadedLargeThumbnail = true;
  159. });
  160. }
  161. });
  162. }
  163. }
  164. void _onFinalImageLoaded(ImageProvider imageProvider, BuildContext context) {
  165. if (mounted) {
  166. precacheImage(imageProvider, context).then((value) {
  167. if (mounted) {
  168. setState(() {
  169. _imageProvider = imageProvider;
  170. _loadedFinalImage = true;
  171. });
  172. }
  173. });
  174. }
  175. }
  176. }