Как создать пользовательский поставщик изображений в flutter?

#flutter

#flutter

Вопрос:

Я хочу написать поставщик изображений для своего собственного класса изображений. Давайте возьмем приведенный ниже виджет в качестве образца для класса изображений. Этот виджет можно использовать всякий раз, когда требуется экземпляр изображения. Теперь я использую виджет, которому в качестве параметра требуется поставщик изображений. Как я могу создать поставщика изображений cusom? (Я знаю о существующих пакетах dart, но вопрос здесь в том, как написать свой собственный поставщик изображений)

 class _AppImageState extends State<AppImage> {
  Uint8List imageData;

  Future download({iterate = 0}) async {
    if (iterate >= 5) {
      Log.w("too many iterations for ${widget.url}");
      return;
    }

    var response;
    try {
      var request = await HttpClient().getUrl(Uri.parse(widget.url));
      response = await request.close();
    } catch (e) {
      Log.e(e);
      return Timer(Duration(seconds: iterate   1), () => download(iterate: iterate   1));
    }

    try {
      if (response != null amp;amp; response.statusCode == 200) {
        Uint8List bytes = await consolidateHttpClientResponseBytes(response);
        setState(() {
          imageData = bytes;
        });
      } else {
        return Timer(Duration(seconds: iterate   1), () => download(iterate: iterate   1));
      }
    } catch (e) {
      Log.e(e);
    }
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    download();
  }

  @override
  Widget build(BuildContext context) {
    return imageData != null ? Image.memory(imageData) : widget.placeholder ?? Container();
  }
}
  

Ответ №1:

Я следую FileImage и пишу свой собственный ImageProvider, я не справляюсь с этим, так как это может вызвать проблемы (например, я не тестирую его с помощью gif), вы также можете прочитать FileImage , это меньше кода, чем NetworkImage и легко читается.

 class CacheImageProvider extends ImageProvider<CacheImageProvider> {
  final String fileId;//the cache id use to get cache

  CacheImageProvider(this.fileId);

  @override
  ImageStreamCompleter load(CacheImageProvider key, DecoderCallback decode) {
    return MultiFrameImageStreamCompleter(
      codec: _loadAsync(decode),
      scale: 1.0,
      debugLabel: fileId,
      informationCollector: () sync* {
        yield ErrorDescription('Path: $fileId');
      },
    );
  }

  Future<Codec> _loadAsync(DecoderCallback decode) async {
    // the DefaultCacheManager() encapsulation, it get cache from local storage.
    final Uint8List bytes = await (await CacheThumbnail.getThumbnail(fileId)).readAsBytes();

    if (bytes.lengthInBytes == 0) {
      // The file may become available later.
      PaintingBinding.instance?.imageCache?.evict(this);
      throw StateError('$fileId is empty and cannot be loaded as an image.');
    }

    return await decode(bytes);
  }

  @override
  Future<CacheImageProvider> obtainKey(ImageConfiguration configuration) {
    return SynchronousFuture<CacheImageProvider>(this);
  }
  
  //the custom == and hashCode must be write, because the ImageProvider memory cache use it to identify the same image.
  @override
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType) return false;
    bool res = other is CacheImageProvider amp;amp; other.fileId == fileId;
    return res;
  }

  @override
  int get hashCode => fileId.hashCode;

  @override
  String toString() => '${objectRuntimeType(this, 'CacheImageProvider')}("$fileId")';
}
  

и использовать его:

 Image(image: CacheImageProvider(_data[index].fileId))
  

Ответ №2:

Вот решение, которое я нашел. Я надеюсь, что это поможет вам

 import 'dart:typed_data';
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class CacheImageProvider extends ImageProvider<CacheImageProvider> {
  final String tag; //the cache id use to get cache
  final Uint8List img; //the bytes of image to cache

  CacheImageProvider(this.tag, this.img);

  @override
  ImageStreamCompleter load(CacheImageProvider key, DecoderCallback decode) {
    return MultiFrameImageStreamCompleter(
      codec: _loadAsync(decode),
      scale: 1.0,
      debugLabel: tag,
      informationCollector: () sync* {
        yield ErrorDescription('Tag: $tag');
      },
    );
  }

  Future<Codec> _loadAsync(DecoderCallback decode) async {
    // the DefaultCacheManager() encapsulation, it get cache from local 
    storage.
    final Uint8List bytes = img;

    if (bytes.lengthInBytes == 0) {
      // The file may become available later.
      PaintingBinding.instance?.imageCache?.evict(this);
      throw StateError('$tag is empty and cannot be loaded as an image.');
    }

    return await decode(bytes);
  }

  @override
  Future<CacheImageProvider> obtainKey(ImageConfiguration configuration) {
    return SynchronousFuture<CacheImageProvider>(this);
  }

  @override
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType) return false;
    bool res = other is CacheImageProvider amp;amp; other.tag == tag;
    return res;
  }

  @override
  int get hashCode => tag.hashCode;

  @override
  String toString() =>
      '${objectRuntimeType(this, 'CacheImageProvider')}("$tag")';
}
  

Ответ №3:

Для тех, кто ищет решение для добавления заголовков в веб-изображения Flutter, я основал свой класс на ответе @nillouise. Удивительно, но это работает!

 
String TOKEN;
String URLBASE;

class AuthImageProvider extends ImageProvider<AuthImageProvider> {
  final String relativeUrl;
  Future<http.Response> resp;

  AuthImageProvider(this.relativeUrl) {
    Map<String, String> h = {"Authorization": "bearer $TOKEN"};
    resp = http.client.get(URLBASE   relativeUrl, headers: h);
  }

  @override
  ImageStreamCompleter load(AuthImageProvider key, decode) {
    return MultiFrameImageStreamCompleter(
        codec: _loadAsync(decode), scale: 1.0);
    throw UnimplementedError();
  }

  Future<Codec> _loadAsync(DecoderCallback decode) async {
    // the DefaultCacheManager() encapsulation, it get cache from local storage.
    final bytes = (await resp).bodyBytes;
    if (bytes.lengthInBytes == 0) {
      // The file may become available later.
      PaintingBinding.instance?.imageCache?.evict(this);
      throw StateError(
          '$relativeUrl is empty and cannot be loaded as an image.');
    }

    return await decode(bytes);
  }

  @override
  Future<AuthImageProvider> obtainKey(ImageConfiguration configuration) {
    return SynchronousFuture<AuthImageProvider>(this);
  }

  @override
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType) return false;
    bool res = other is AuthImageProvider amp;amp; other.relativeUrl == relativeUrl;
    return res;
  }

  @override
  int get hashCode => relativeUrl.hashCode;

  @override
  String toString() =>
      '${objectRuntimeType(this, 'CacheImageProvider')}("$relativeUrl")';
}