import 'dart:ui' as ui; import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:network_image_cached/src/http_request.dart'; import 'src/bloc/cashed_image_bloc.dart' show CashedImageBloc, CashedImageGetErrorState, CashedImageGetState, CashedImageState, GetStartImageEvent; class CachedNetworkImageWidget extends StatelessWidget { final String imageUrl; final int count; final BoxFit fit; final double? width; final double? height; final Widget? placeholder; final Widget? errorWidget; final bool cached; final String? cachkey; final Widget? loadwidget; const CachedNetworkImageWidget({ super.key, required this.imageUrl, required this.cachkey, required this.fit, this.count = 10, this.height, this.width, this.errorWidget, this.placeholder, this.cached = true, this.loadwidget = const CircularProgressIndicator(), }); @override Widget build(BuildContext context) { return BlocProvider( create: (context) => CashedImageBloc() ..add( GetStartImageEvent( url: imageUrl, cached: cached, count: count, cachkey: cachkey, ), ), child: Builder( builder: (context) { return BlocBuilder( builder: (context, state) { if (state is CashedImageGetErrorState) { return SizedBox( width: width, height: height, child: errorWidget ?? Center(child: Icon(Icons.error)), ); } if (state is CashedImageGetState) { return Image.memory( state.bytes, fit: fit, width: width, height: height, ); } return SizedBox( width: width, height: height, child: placeholder ?? Center(child: CircularProgressIndicator()), ); }, ); }, ), ); } } class CachedNetworkImageProvider extends ImageProvider { final String imageUrl; final int count; final BoxFit fit; final double? width; final double? height; final Widget? placeholder; final Widget? errorWidget; final bool cached; final String? cachkey; final Widget? loadwidget; const CachedNetworkImageProvider({ required this.imageUrl, required this.cachkey, required this.fit, this.count = 10, this.height, this.width, this.errorWidget, this.placeholder, this.cached = true, this.loadwidget = const CircularProgressIndicator(), }); @override ImageStreamCompleter loadImage( CachedNetworkImageProvider key, ImageDecoderCallback decode, ) { // 1. Создаём Future, который загрузит и декодирует изображение // 2. Возвращаем ImageStreamCompleter, который управляет потоком загрузки return MultiFrameImageStreamCompleter( codec: _loadAndDecodeImage(decode), scale: 1.0, informationCollector: () => [ DiagnosticsProperty( 'Image provider', key, showName: false, ), ], ); } // Вспомогательный метод: загружает байты и декодирует в ui.Image Future _loadAndDecodeImage(ImageDecoderCallback decode) async { try { // 1. Отправляем HTTP‑запрос HttpGetImage httpGetImage = HttpGetImage(url: imageUrl, count: count); // 2. Декодируем байты в ui.Image Uint8List img = await httpGetImage.getDataObjectIsolate(); final decodedImage = await decode( await ImmutableBuffer.fromUint8List(img), ); return decodedImage; } catch (e) { // Если ошибка — бросаем исключение (будет обработано в Image widget) throw Exception('Failed to load image: $e'); } } @override Future obtainKey( ImageConfiguration configuration, ) { return SynchronousFuture(this); } }