thumbnail_image.dart 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import 'package:auto_route/auto_route.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:hooks_riverpod/hooks_riverpod.dart';
  5. import 'package:immich_mobile/modules/favorite/providers/favorite_provider.dart';
  6. import 'package:immich_mobile/routing/router.dart';
  7. import 'package:immich_mobile/shared/models/asset.dart';
  8. import 'package:immich_mobile/shared/ui/immich_image.dart';
  9. import 'package:immich_mobile/utils/storage_indicator.dart';
  10. class ThumbnailImage extends HookConsumerWidget {
  11. final Asset asset;
  12. final List<Asset> assetList;
  13. final bool showStorageIndicator;
  14. final bool useGrayBoxPlaceholder;
  15. final bool isSelected;
  16. final bool multiselectEnabled;
  17. final Function? onSelect;
  18. final Function? onDeselect;
  19. const ThumbnailImage({
  20. Key? key,
  21. required this.asset,
  22. required this.assetList,
  23. this.showStorageIndicator = true,
  24. this.useGrayBoxPlaceholder = false,
  25. this.isSelected = false,
  26. this.multiselectEnabled = false,
  27. this.onDeselect,
  28. this.onSelect,
  29. }) : super(key: key);
  30. @override
  31. Widget build(BuildContext context, WidgetRef ref) {
  32. Widget buildSelectionIcon(Asset asset) {
  33. if (isSelected) {
  34. return Icon(
  35. Icons.check_circle,
  36. color: Theme.of(context).primaryColor,
  37. );
  38. } else {
  39. return const Icon(
  40. Icons.circle_outlined,
  41. color: Colors.white,
  42. );
  43. }
  44. }
  45. return GestureDetector(
  46. onTap: () {
  47. if (multiselectEnabled) {
  48. if (isSelected) {
  49. onDeselect?.call();
  50. } else {
  51. onSelect?.call();
  52. }
  53. } else {
  54. AutoRouter.of(context).push(
  55. GalleryViewerRoute(
  56. assetList: assetList,
  57. asset: asset,
  58. ),
  59. );
  60. }
  61. },
  62. onLongPress: () {
  63. onSelect?.call();
  64. HapticFeedback.heavyImpact();
  65. },
  66. child: Hero(
  67. createRectTween: (begin, end) {
  68. double? top;
  69. // Uses the [BoxFit.contain] algorithm
  70. if (asset.width != null && asset.height != null) {
  71. final assetAR = asset.width! / asset.height!;
  72. final w = MediaQuery.of(context).size.width;
  73. final deviceAR = MediaQuery.of(context).size.aspectRatio;
  74. if (deviceAR < assetAR) {
  75. top = asset.height! * w / asset.width!;
  76. } else {
  77. top = 0;
  78. }
  79. // get the height offset
  80. }
  81. return MaterialRectCenterArcTween(
  82. begin: Rect.fromLTRB(
  83. 0,
  84. top ?? 0.0,
  85. MediaQuery.of(context).size.width,
  86. MediaQuery.of(context).size.height,
  87. ),
  88. end: end,
  89. );
  90. },
  91. tag: asset.id,
  92. child: Stack(
  93. children: [
  94. Container(
  95. decoration: BoxDecoration(
  96. border: multiselectEnabled && isSelected
  97. ? Border.all(
  98. color: Theme.of(context).primaryColorLight,
  99. width: 10,
  100. )
  101. : const Border(),
  102. ),
  103. child: ImmichImage(
  104. asset,
  105. width: 300,
  106. height: 300,
  107. useGrayBoxPlaceholder: useGrayBoxPlaceholder,
  108. ),
  109. ),
  110. if (multiselectEnabled)
  111. Padding(
  112. padding: const EdgeInsets.all(3.0),
  113. child: Align(
  114. alignment: Alignment.topLeft,
  115. child: buildSelectionIcon(asset),
  116. ),
  117. ),
  118. if (showStorageIndicator)
  119. Positioned(
  120. right: 10,
  121. bottom: 5,
  122. child: Icon(
  123. storageIcon(asset),
  124. color: Colors.white,
  125. size: 18,
  126. ),
  127. ),
  128. if (ref.watch(favoriteProvider).contains(asset.id))
  129. const Positioned(
  130. left: 10,
  131. bottom: 5,
  132. child: Icon(
  133. Icons.star,
  134. color: Colors.white,
  135. size: 18,
  136. ),
  137. ),
  138. if (!asset.isImage)
  139. Positioned(
  140. top: 5,
  141. right: 5,
  142. child: Row(
  143. children: [
  144. Text(
  145. asset.duration.toString().substring(0, 7),
  146. style: const TextStyle(
  147. color: Colors.white,
  148. fontSize: 10,
  149. ),
  150. ),
  151. const Icon(
  152. Icons.play_circle_outline_rounded,
  153. color: Colors.white,
  154. ),
  155. ],
  156. ),
  157. ),
  158. ],
  159. ),
  160. ),
  161. );
  162. }
  163. }