thumbnail_widget.dart 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_image_compress/flutter_image_compress.dart';
  3. import 'package:photos/core/cache/image_cache.dart';
  4. import 'package:photos/core/cache/thumbnail_cache.dart';
  5. import 'package:photos/db/files_db.dart';
  6. import 'package:photos/models/file.dart';
  7. import 'package:logging/logging.dart';
  8. import 'package:photos/core/constants.dart';
  9. import 'package:photos/models/file_type.dart';
  10. import 'package:photos/repositories/file_repository.dart';
  11. import 'package:photos/utils/file_util.dart';
  12. class ThumbnailWidget extends StatefulWidget {
  13. final File file;
  14. final BoxFit fit;
  15. const ThumbnailWidget(
  16. this.file, {
  17. Key key,
  18. this.fit = BoxFit.cover,
  19. }) : super(key: key);
  20. @override
  21. _ThumbnailWidgetState createState() => _ThumbnailWidgetState();
  22. }
  23. class _ThumbnailWidgetState extends State<ThumbnailWidget> {
  24. static final _logger = Logger("ThumbnailWidget");
  25. static final Widget loadingWidget = Container(
  26. alignment: Alignment.center,
  27. color: Colors.grey[800],
  28. );
  29. bool _hasLoadedThumbnail = false;
  30. bool _isLoadingThumbnail = false;
  31. bool _encounteredErrorLoadingThumbnail = false;
  32. ImageProvider _imageProvider;
  33. @override
  34. void initState() {
  35. super.initState();
  36. }
  37. @override
  38. Widget build(BuildContext context) {
  39. if (widget.file.localID == null) {
  40. _loadNetworkImage();
  41. } else {
  42. _loadLocalImage(context);
  43. }
  44. var image;
  45. if (_imageProvider != null) {
  46. image = Image(
  47. image: _imageProvider,
  48. fit: widget.fit,
  49. );
  50. }
  51. var content;
  52. if (image != null) {
  53. if (widget.file.fileType == FileType.video) {
  54. content = Stack(
  55. children: [
  56. image,
  57. Icon(Icons.play_circle_outline),
  58. ],
  59. fit: StackFit.expand,
  60. );
  61. } else {
  62. content = image;
  63. }
  64. }
  65. return Stack(
  66. children: [
  67. loadingWidget,
  68. AnimatedOpacity(
  69. opacity: content == null ? 0 : 1.0,
  70. duration: Duration(milliseconds: 400),
  71. child: content,
  72. ),
  73. ],
  74. fit: StackFit.expand,
  75. );
  76. }
  77. void _loadLocalImage(BuildContext context) {
  78. if (!_hasLoadedThumbnail &&
  79. !_encounteredErrorLoadingThumbnail &&
  80. !_isLoadingThumbnail) {
  81. _isLoadingThumbnail = true;
  82. final cachedSmallThumbnail =
  83. ThumbnailLruCache.get(widget.file, THUMBNAIL_SMALL_SIZE);
  84. if (cachedSmallThumbnail != null) {
  85. _imageProvider = Image.memory(cachedSmallThumbnail).image;
  86. _hasLoadedThumbnail = true;
  87. } else {
  88. widget.file.getAsset().then((asset) async {
  89. if (asset == null || !(await asset.exists)) {
  90. if (widget.file.uploadedFileID != null) {
  91. widget.file.localID = null;
  92. FilesDB.instance.update(widget.file);
  93. _loadNetworkImage();
  94. } else {
  95. FilesDB.instance.deleteLocalFile(widget.file.localID);
  96. FileRepository.instance.reloadFiles();
  97. }
  98. return;
  99. }
  100. asset
  101. .thumbDataWithSize(
  102. THUMBNAIL_SMALL_SIZE,
  103. THUMBNAIL_SMALL_SIZE,
  104. quality: THUMBNAIL_SMALL_SIZE_QUALITY,
  105. )
  106. .then((data) {
  107. if (data != null && mounted) {
  108. final imageProvider = Image.memory(data).image;
  109. precacheImage(imageProvider, context).then((value) {
  110. if (mounted) {
  111. setState(() {
  112. _imageProvider = imageProvider;
  113. _hasLoadedThumbnail = true;
  114. });
  115. }
  116. });
  117. }
  118. ThumbnailLruCache.put(widget.file, THUMBNAIL_SMALL_SIZE, data);
  119. });
  120. }).catchError((e) {
  121. _logger.warning("Could not load image: ", e);
  122. _encounteredErrorLoadingThumbnail = true;
  123. });
  124. }
  125. }
  126. }
  127. void _loadNetworkImage() {
  128. if (!_hasLoadedThumbnail &&
  129. !_encounteredErrorLoadingThumbnail &&
  130. !_isLoadingThumbnail) {
  131. _isLoadingThumbnail = true;
  132. final cachedThumbnail = ThumbnailFileLruCache.get(widget.file);
  133. if (cachedThumbnail != null) {
  134. _imageProvider = Image.file(cachedThumbnail).image;
  135. _hasLoadedThumbnail = true;
  136. return;
  137. }
  138. _getThumbnailFromServer();
  139. }
  140. }
  141. void _getThumbnailFromServer() {
  142. getThumbnailFromServer(widget.file).then((file) async {
  143. var imageProvider;
  144. if (file.lengthSync() > THUMBNAIL_DATA_LIMIT) {
  145. final compressed = await FlutterImageCompress.compressWithFile(
  146. file.path,
  147. quality: 25,
  148. minHeight: THUMBNAIL_SMALL_SIZE,
  149. minWidth: THUMBNAIL_SMALL_SIZE,
  150. );
  151. imageProvider = Image.memory(compressed).image;
  152. } else {
  153. imageProvider = Image.file(file).image;
  154. }
  155. if (mounted) {
  156. precacheImage(imageProvider, context).then((value) {
  157. if (mounted) {
  158. setState(() {
  159. _imageProvider = imageProvider;
  160. _hasLoadedThumbnail = true;
  161. });
  162. }
  163. }).catchError((e) {
  164. _logger.severe("Could not load image " + widget.file.toString());
  165. _encounteredErrorLoadingThumbnail = true;
  166. });
  167. }
  168. });
  169. }
  170. @override
  171. void didUpdateWidget(ThumbnailWidget oldWidget) {
  172. super.didUpdateWidget(oldWidget);
  173. if (widget.file.generatedID != oldWidget.file.generatedID) {
  174. setState(() {
  175. _hasLoadedThumbnail = false;
  176. _isLoadingThumbnail = false;
  177. _imageProvider = null;
  178. });
  179. }
  180. }
  181. }