immich_image.dart 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import 'package:cached_network_image/cached_network_image.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:hive_flutter/hive_flutter.dart';
  5. import 'package:immich_mobile/constants/hive_box.dart';
  6. import 'package:immich_mobile/shared/models/asset.dart';
  7. import 'package:immich_mobile/utils/image_url_builder.dart';
  8. import 'package:photo_manager/photo_manager.dart';
  9. /// Renders an Asset using local data if available, else remote data
  10. class ImmichImage extends StatelessWidget {
  11. const ImmichImage(
  12. this.asset, {
  13. required this.width,
  14. required this.height,
  15. this.useGrayBoxPlaceholder = false,
  16. super.key,
  17. });
  18. final Asset? asset;
  19. final bool useGrayBoxPlaceholder;
  20. final double width;
  21. final double height;
  22. @override
  23. Widget build(BuildContext context) {
  24. if (this.asset == null) {
  25. return Container(
  26. decoration: const BoxDecoration(
  27. color: Colors.grey,
  28. ),
  29. child: SizedBox(
  30. width: width,
  31. height: height,
  32. child: const Center(
  33. child: Icon(Icons.no_photography),
  34. ),
  35. ),
  36. );
  37. }
  38. final Asset asset = this.asset!;
  39. if (asset.isLocal) {
  40. return Image(
  41. image: AssetEntityImageProvider(
  42. asset.local!,
  43. isOriginal: false,
  44. thumbnailSize: const ThumbnailSize.square(250), // like server thumbs
  45. ),
  46. width: width,
  47. height: height,
  48. fit: BoxFit.cover,
  49. frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
  50. if (wasSynchronouslyLoaded || frame != null) {
  51. return child;
  52. }
  53. return (useGrayBoxPlaceholder
  54. ? const SizedBox.square(
  55. dimension: 250,
  56. child: DecoratedBox(
  57. decoration: BoxDecoration(color: Colors.grey),
  58. ),
  59. )
  60. : Transform.scale(
  61. scale: 0.2,
  62. child: const CircularProgressIndicator(),
  63. ));
  64. },
  65. errorBuilder: (context, error, stackTrace) {
  66. if (error is PlatformException &&
  67. error.code == "The asset not found!") {
  68. debugPrint(
  69. "Asset ${asset.localId} does not exist anymore on device!",
  70. );
  71. } else {
  72. debugPrint(
  73. "Error getting thumb for assetId=${asset.localId}: $error",
  74. );
  75. }
  76. return Icon(
  77. Icons.image_not_supported_outlined,
  78. color: Theme.of(context).primaryColor,
  79. );
  80. },
  81. );
  82. }
  83. final String? token = Hive.box(userInfoBox).get(accessTokenKey);
  84. final String thumbnailRequestUrl = getThumbnailUrl(asset);
  85. return CachedNetworkImage(
  86. imageUrl: thumbnailRequestUrl,
  87. httpHeaders: {"Authorization": "Bearer $token"},
  88. cacheKey: getThumbnailCacheKey(asset),
  89. width: width,
  90. height: height,
  91. // keeping memCacheWidth, memCacheHeight, maxWidthDiskCache and
  92. // maxHeightDiskCache = null allows to simply store the webp thumbnail
  93. // from the server and use it for all rendered thumbnail sizes
  94. fit: BoxFit.cover,
  95. fadeInDuration: const Duration(milliseconds: 250),
  96. progressIndicatorBuilder: (context, url, downloadProgress) {
  97. if (useGrayBoxPlaceholder) {
  98. return const DecoratedBox(
  99. decoration: BoxDecoration(color: Colors.grey),
  100. );
  101. }
  102. return Transform.scale(
  103. scale: 0.2,
  104. child: CircularProgressIndicator(
  105. value: downloadProgress.progress,
  106. ),
  107. );
  108. },
  109. errorWidget: (context, url, error) {
  110. debugPrint("Error getting thumbnail $url = $error");
  111. CachedNetworkImage.evictFromCache(thumbnailRequestUrl);
  112. return Icon(
  113. Icons.image_not_supported_outlined,
  114. color: Theme.of(context).primaryColor,
  115. );
  116. },
  117. );
  118. }
  119. }