ix_image.dart 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import 'package:cached_network_image/cached_network_image.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:nomo/app/extensions/img_extension.dart';
  4. import 'package:shimmer/shimmer.dart';
  5. import 'dart:io';
  6. import '../constants/configs.dart';
  7. enum ImageSourceType { network, asset, file }
  8. class IXImage extends StatelessWidget {
  9. const IXImage({
  10. super.key,
  11. required this.source,
  12. required this.width,
  13. required this.height,
  14. this.sourceType = ImageSourceType.network,
  15. this.fadeOutDuration,
  16. this.fadeInDuration,
  17. this.origAspectRatio,
  18. this.borderRadius,
  19. this.fit,
  20. this.placeholderDuration = const Duration(milliseconds: 400),
  21. });
  22. final String source;
  23. final ImageSourceType sourceType;
  24. final double width;
  25. final double height;
  26. final Duration? fadeOutDuration;
  27. final Duration? fadeInDuration;
  28. final double? origAspectRatio;
  29. final double? borderRadius;
  30. final BoxFit? fit;
  31. final Duration placeholderDuration;
  32. @override
  33. Widget build(BuildContext context) {
  34. int? memCacheWidth, memCacheHeight;
  35. double aspectRatio = (width / height).toDouble();
  36. void setMemCacheSizes() {
  37. if (aspectRatio > 1) {
  38. memCacheHeight = height.cacheSize(context);
  39. } else if (aspectRatio < 1) {
  40. memCacheWidth = width.cacheSize(context);
  41. } else {
  42. if (origAspectRatio != null && origAspectRatio! > 1) {
  43. memCacheWidth = width.cacheSize(context);
  44. } else if (origAspectRatio != null && origAspectRatio! < 1) {
  45. memCacheHeight = height.cacheSize(context);
  46. } else {
  47. memCacheWidth = width.cacheSize(context);
  48. memCacheHeight = height.cacheSize(context);
  49. }
  50. }
  51. }
  52. setMemCacheSizes();
  53. if (memCacheWidth == null && memCacheHeight == null) {
  54. memCacheWidth = width.toInt();
  55. }
  56. return ClipRRect(
  57. clipBehavior: Clip.antiAlias,
  58. borderRadius: BorderRadius.circular(borderRadius ?? 0),
  59. child: _buildImage(context, memCacheWidth, memCacheHeight),
  60. );
  61. }
  62. Widget _buildImage(
  63. BuildContext context,
  64. int? memCacheWidth,
  65. int? memCacheHeight,
  66. ) {
  67. switch (sourceType) {
  68. case ImageSourceType.network:
  69. String url = source;
  70. if (source.indexOf("http") != 0) {
  71. url = "${Configs.assetUrl}/$source";
  72. }
  73. return CachedNetworkImage(
  74. key: ValueKey(url),
  75. imageUrl: url,
  76. width: width,
  77. height: height,
  78. memCacheWidth: memCacheWidth,
  79. memCacheHeight: memCacheHeight,
  80. fit: fit ?? BoxFit.cover,
  81. fadeOutDuration: fadeOutDuration ?? const Duration(milliseconds: 250),
  82. fadeInDuration: fadeInDuration ?? const Duration(milliseconds: 350),
  83. filterQuality: FilterQuality.medium,
  84. errorWidget: (context, url, error) =>
  85. _buildErrorWidget(context, url, error),
  86. placeholder: (context, url) => _buildPlaceholder(context, url),
  87. );
  88. case ImageSourceType.asset:
  89. return Image.asset(
  90. source,
  91. width: width,
  92. height: height,
  93. fit: fit ?? BoxFit.cover,
  94. alignment: fit == BoxFit.fitWidth
  95. ? Alignment.topCenter
  96. : Alignment.center,
  97. errorBuilder: (context, error, stackTrace) =>
  98. _buildErrorWidget(context, source, error),
  99. );
  100. case ImageSourceType.file:
  101. return Image.file(
  102. File(source),
  103. width: width,
  104. height: height,
  105. fit: fit ?? BoxFit.cover,
  106. errorBuilder: (context, error, stackTrace) =>
  107. _buildErrorWidget(context, source, error),
  108. );
  109. }
  110. }
  111. Widget _buildErrorWidget(BuildContext context, String url, dynamic error) {
  112. return _buildSmoothPlaceholder(context, isError: false);
  113. }
  114. Widget _buildPlaceholder(BuildContext context, String url) {
  115. return _buildSmoothPlaceholder(context, isError: false);
  116. }
  117. Widget _buildSmoothPlaceholder(
  118. BuildContext context, {
  119. required bool isError,
  120. }) {
  121. return AnimatedContainer(
  122. duration: placeholderDuration,
  123. curve: Curves.easeInOut,
  124. width: width,
  125. height: height,
  126. decoration: BoxDecoration(
  127. gradient: LinearGradient(
  128. begin: Alignment.topLeft,
  129. end: Alignment.bottomRight,
  130. colors: isError
  131. ? [
  132. Colors.red.withValues(alpha: 0.15),
  133. Colors.red.withValues(alpha: 0.08),
  134. Colors.red.withValues(alpha: 0.15),
  135. ]
  136. : [
  137. Colors.grey.withValues(alpha: 0.25),
  138. Colors.grey.withValues(alpha: 0.08),
  139. Colors.grey.withValues(alpha: 0.25),
  140. ],
  141. stops: const [0.0, 0.5, 1.0],
  142. ),
  143. borderRadius: BorderRadius.circular(borderRadius ?? 0),
  144. ),
  145. child: Shimmer.fromColors(
  146. baseColor: isError
  147. ? Colors.red.withValues(alpha: 0.3)
  148. : Colors.grey.withValues(alpha: 0.4),
  149. highlightColor: isError
  150. ? Colors.red.withValues(alpha: 0.1)
  151. : Colors.grey.withValues(alpha: 0.1),
  152. period: const Duration(milliseconds: 1800),
  153. child: Container(
  154. width: width,
  155. height: height,
  156. decoration: BoxDecoration(
  157. color: Colors.white.withValues(alpha: 0.15),
  158. borderRadius: BorderRadius.circular(borderRadius ?? 0),
  159. ),
  160. ),
  161. ),
  162. );
  163. }
  164. }