| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- 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:flutter_cache_manager/flutter_cache_manager.dart';
- import 'package:network_image_cached/src/http_request.dart';
- import 'bloc/cashed_image_bloc.dart'
- show
- CashedImageBloc,
- CashedImageGetErrorState,
- CashedImageGetState,
- CashedImageState,
- GetStartImageEvent;
- typedef ImageWidgetBuilder =
- Widget Function(BuildContext context, ImageProvider imageProvider);
- class CachedNetworkImageWidget extends StatefulWidget {
- final String imageUrl;
- final int count;
- final double? width;
- final double? height;
- final Widget? placeholder;
- final Widget? errorWidget;
- final bool cached;
- final Map<String, String>? httpHeaders;
- final String? cacheKey;
- final Widget? loadwidget;
- final ImageWidgetBuilder imageBuilder;
- const CachedNetworkImageWidget({
- super.key,
- required this.imageUrl,
- required this.cacheKey,
- this.count = 10,
- this.height,
- this.width,
- this.errorWidget,
- this.placeholder,
- this.cached = true,
- this.loadwidget = const CircularProgressIndicator(),
- this.httpHeaders,
- required this.imageBuilder,
- });
- @override
- CachedNetworkImageWidgetState createState() =>
- CachedNetworkImageWidgetState();
- }
- class CachedNetworkImageWidgetState extends State<CachedNetworkImageWidget>
- with AutomaticKeepAliveClientMixin {
- late final CashedImageBloc bloc;
- @override
- void initState() {
- if (!mounted) {
- bloc = CashedImageBloc()
- ..add(
- GetStartImageEvent(
- httpHeaders: widget.httpHeaders,
- url: widget.imageUrl,
- cached: widget.cached,
- count: widget.count,
- cachkey: widget.cacheKey,
- ),
- );
- }
- super.initState();
- }
- @override
- void dispose() {
- bloc.close();
- super.dispose();
- }
- @override
- bool get wantKeepAlive => true;
- @override
- Widget build(BuildContext context) {
- super.build(context);
- return BlocProvider(
- create: (context) => bloc,
- child: BlocBuilder<CashedImageBloc, CashedImageState>(
- builder: (context, state) {
- if (state is CashedImageGetErrorState) {
- return SizedBox(
- width: widget.width,
- height: widget.height,
- child: widget.errorWidget ?? Center(child: Icon(Icons.error)),
- );
- }
- if (state is CashedImageGetState) {
- ImageProvider imageProvider = MemoryImage(state.bytes);
- return widget.imageBuilder(context, imageProvider!);
- }
- return SizedBox(
- width: widget.width,
- height: widget.height,
- child:
- widget.placeholder ??
- Center(child: widget.loadwidget ?? CircularProgressIndicator()),
- );
- },
- ),
- );
- }
- }
- class CachedNetworkImageProvider
- extends ImageProvider<CachedNetworkImageProvider> {
- 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? cacheKey;
- final Widget? loadwidget;
- final Map<String, String>? httpHeaders;
- const CachedNetworkImageProvider({
- required this.imageUrl,
- required this.cacheKey,
- required this.fit,
- this.count = 10,
- this.height,
- this.width,
- this.errorWidget,
- this.placeholder,
- this.cached = true,
- this.loadwidget = const CircularProgressIndicator(),
- this.httpHeaders,
- });
- @override
- ImageStreamCompleter loadImage(
- CachedNetworkImageProvider key,
- ImageDecoderCallback decode,
- ) {
- // 1. Создаём Future, который загрузит и декодирует изображение
- // 2. Возвращаем ImageStreamCompleter, который управляет потоком загрузки
- return MultiFrameImageStreamCompleter(
- codec: _loadAndDecodeImage(decode),
- scale: 1.0,
- informationCollector: () => <DiagnosticsNode>[
- DiagnosticsProperty<CachedNetworkImageProvider>(
- 'Image provider',
- key,
- showName: false,
- ),
- ],
- );
- }
- // Вспомогательный метод: загружает байты и декодирует в ui.Image
- Future<ui.Codec> _loadAndDecodeImage(ImageDecoderCallback decode) async {
- try {
- // 1. Отправляем HTTP‑запрос
- HttpGetImage httpGetImage = HttpGetImage(
- url: imageUrl,
- count: count,
- httpHeaders: httpHeaders,
- );
- // 2. Декодируем байты в ui.Image
- DefaultCacheManager defaultCacheManager = DefaultCacheManager();
- if (cached) {
- FileInfo? fileInfo;
- if (cacheKey != null) {
- fileInfo = await defaultCacheManager.getFileFromCache(cacheKey!);
- } else {
- fileInfo = await defaultCacheManager.getFileFromCache(imageUrl);
- }
- if (fileInfo != null) {
- final decodedImage = await decode(
- await ImmutableBuffer.fromUint8List(
- await fileInfo.file.readAsBytes(),
- ),
- );
- return decodedImage;
- }
- }
- Uint8List img;
- try {
- img = await httpGetImage.getDataObjectIsolate();
- if (cached) {
- if (cacheKey != null) {
- defaultCacheManager.putFile(cacheKey!, img);
- } else {
- defaultCacheManager.putFile(imageUrl, img);
- }
- }
- } catch (e) {
- throw Exception('Failed to load image: $e');
- }
- final decodedImage = await decode(
- await ImmutableBuffer.fromUint8List(img),
- );
- return decodedImage;
- } catch (e) {
- // Если ошибка — бросаем исключение (будет обработано в Image widget)
- throw Exception('Failed to load image: $e');
- }
- }
- @override
- Future<CachedNetworkImageProvider> obtainKey(
- ImageConfiguration configuration,
- ) {
- return SynchronousFuture<CachedNetworkImageProvider>(this);
- }
- }
|