1
0

CashedNetworkImageWidget.dart 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import 'dart:ui' as ui;
  2. import 'dart:ui';
  3. import 'package:flutter/foundation.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter_bloc/flutter_bloc.dart';
  6. import 'package:network_image_cached/src/http_request.dart';
  7. import 'bloc/cashed_image_bloc.dart'
  8. show
  9. CashedImageBloc,
  10. CashedImageGetErrorState,
  11. CashedImageGetState,
  12. CashedImageState,
  13. GetStartImageEvent;
  14. typedef ImageWidgetBuilder =
  15. Widget Function(BuildContext context, ImageProvider imageProvider);
  16. class CachedNetworkImageWidget extends StatelessWidget {
  17. final String imageUrl;
  18. final int count;
  19. final BoxFit fit;
  20. final double? width;
  21. final double? height;
  22. final Widget? placeholder;
  23. final Widget? errorWidget;
  24. final bool cached;
  25. final Map<String, String>? httpHeaders;
  26. final String? cacheKey;
  27. final Widget? loadwidget;
  28. final ImageWidgetBuilder imageBuilder;
  29. const CachedNetworkImageWidget({
  30. super.key,
  31. required this.imageUrl,
  32. required this.cacheKey,
  33. required this.fit,
  34. this.count = 10,
  35. this.height,
  36. this.width,
  37. this.errorWidget,
  38. this.placeholder,
  39. this.cached = true,
  40. this.loadwidget = const CircularProgressIndicator(),
  41. this.httpHeaders,
  42. required this.imageBuilder,
  43. });
  44. @override
  45. Widget build(BuildContext context) {
  46. return BlocProvider(
  47. create: (context) => CashedImageBloc()
  48. ..add(
  49. GetStartImageEvent(
  50. url: imageUrl,
  51. cached: cached,
  52. count: count,
  53. cachkey: cacheKey,
  54. ),
  55. ),
  56. child: Builder(
  57. builder: (context) {
  58. return BlocBuilder<CashedImageBloc, CashedImageState>(
  59. builder: (context, state) {
  60. if (state is CashedImageGetErrorState) {
  61. return SizedBox(
  62. width: width,
  63. height: height,
  64. child: errorWidget ?? Center(child: Icon(Icons.error)),
  65. );
  66. }
  67. if (state is CashedImageGetState) {
  68. ImageProvider imageProvider = MemoryImage(state.bytes);
  69. return imageBuilder(context, imageProvider);
  70. }
  71. return SizedBox(
  72. width: width,
  73. height: height,
  74. child:
  75. placeholder ?? Center(child: CircularProgressIndicator()),
  76. );
  77. },
  78. );
  79. },
  80. ),
  81. );
  82. }
  83. }
  84. class CachedNetworkImageProvider
  85. extends ImageProvider<CachedNetworkImageProvider> {
  86. final String imageUrl;
  87. final int count;
  88. final BoxFit fit;
  89. final double? width;
  90. final double? height;
  91. final Widget? placeholder;
  92. final Widget? errorWidget;
  93. final bool cached;
  94. final String? cacheKey;
  95. final Widget? loadwidget;
  96. final Map<String, String>? httpHeaders;
  97. const CachedNetworkImageProvider({
  98. required this.imageUrl,
  99. required this.cacheKey,
  100. required this.fit,
  101. this.count = 10,
  102. this.height,
  103. this.width,
  104. this.errorWidget,
  105. this.placeholder,
  106. this.cached = true,
  107. this.loadwidget = const CircularProgressIndicator(),
  108. this.httpHeaders,
  109. });
  110. @override
  111. ImageStreamCompleter loadImage(
  112. CachedNetworkImageProvider key,
  113. ImageDecoderCallback decode,
  114. ) {
  115. // 1. Создаём Future, который загрузит и декодирует изображение
  116. // 2. Возвращаем ImageStreamCompleter, который управляет потоком загрузки
  117. return MultiFrameImageStreamCompleter(
  118. codec: _loadAndDecodeImage(decode),
  119. scale: 1.0,
  120. informationCollector: () => <DiagnosticsNode>[
  121. DiagnosticsProperty<CachedNetworkImageProvider>(
  122. 'Image provider',
  123. key,
  124. showName: false,
  125. ),
  126. ],
  127. );
  128. }
  129. // Вспомогательный метод: загружает байты и декодирует в ui.Image
  130. Future<ui.Codec> _loadAndDecodeImage(ImageDecoderCallback decode) async {
  131. try {
  132. // 1. Отправляем HTTP‑запрос
  133. HttpGetImage httpGetImage = HttpGetImage(
  134. url: imageUrl,
  135. count: count,
  136. httpHeaders: httpHeaders,
  137. );
  138. // 2. Декодируем байты в ui.Image
  139. Uint8List img = await httpGetImage.getDataObjectIsolate();
  140. final decodedImage = await decode(
  141. await ImmutableBuffer.fromUint8List(img),
  142. );
  143. return decodedImage;
  144. } catch (e) {
  145. // Если ошибка — бросаем исключение (будет обработано в Image widget)
  146. throw Exception('Failed to load image: $e');
  147. }
  148. }
  149. @override
  150. Future<CachedNetworkImageProvider> obtainKey(
  151. ImageConfiguration configuration,
  152. ) {
  153. return SynchronousFuture<CachedNetworkImageProvider>(this);
  154. }
  155. }