|
@@ -1,223 +0,0 @@
|
|
|
-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 CachedNetworkImage 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 CachedNetworkImage({
|
|
|
|
|
- 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
|
|
|
|
|
- CachedNetworkImageState createState() => CachedNetworkImageState();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-class CachedNetworkImageState extends State<CachedNetworkImage>
|
|
|
|
|
- with AutomaticKeepAliveClientMixin {
|
|
|
|
|
- late final CashedImageBloc bloc;
|
|
|
|
|
-
|
|
|
|
|
- @override
|
|
|
|
|
- void initState() {
|
|
|
|
|
- bloc = CashedImageBloc();
|
|
|
|
|
-
|
|
|
|
|
- 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
|
|
|
|
|
- ..add(
|
|
|
|
|
- GetStartImageEvent(
|
|
|
|
|
- httpHeaders: widget.httpHeaders,
|
|
|
|
|
- url: widget.imageUrl,
|
|
|
|
|
- cached: widget.cached,
|
|
|
|
|
- count: widget.count,
|
|
|
|
|
- cachkey: widget.cacheKey,
|
|
|
|
|
- ),
|
|
|
|
|
- ),
|
|
|
|
|
- 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 CachedNetworkImageProvide
|
|
|
|
|
- extends ImageProvider<CachedNetworkImageProvide> {
|
|
|
|
|
- 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 CachedNetworkImageProvide({
|
|
|
|
|
- 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(
|
|
|
|
|
- CachedNetworkImageProvide key,
|
|
|
|
|
- ImageDecoderCallback decode,
|
|
|
|
|
- ) {
|
|
|
|
|
- // 1. Создаём Future, который загрузит и декодирует изображение
|
|
|
|
|
-
|
|
|
|
|
- // 2. Возвращаем ImageStreamCompleter, который управляет потоком загрузки
|
|
|
|
|
- return MultiFrameImageStreamCompleter(
|
|
|
|
|
- codec: _loadAndDecodeImage(decode),
|
|
|
|
|
- scale: 1.0,
|
|
|
|
|
- informationCollector: () => <DiagnosticsNode>[
|
|
|
|
|
- DiagnosticsProperty<CachedNetworkImageProvide>(
|
|
|
|
|
- '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<CachedNetworkImageProvide> obtainKey(
|
|
|
|
|
- ImageConfiguration configuration,
|
|
|
|
|
- ) {
|
|
|
|
|
- return SynchronousFuture<CachedNetworkImageProvide>(this);
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|