CachedNetworkImageProviderのキャッシュ機能とGif制御を両立させる
目的
cached_network_image package にあるCachedNetworkImageProvider で実現されているキャッシュ機能を用いてGifをループさせずに表示させたい。
背景
cached_network_image packageのキャッシュ機能を利用したWidgetとして
CachedNetworkImageが用意されている。これを用いてGifを表示自体は可能だが、Gifを制御する機能を持たないためGifをループさせずに表示させることができない。
ループ制御などが可能な既存のGif表示パッケージには
などがあるがいずれもcached_network_imageと競合していてうまくいかない概要
・CachedNetworkImageProviderを拡張して総フレーム数を返すframeCountプロパティを生やす
・標準のImage Widgetが引数に持つframeBuilderで、現在表示中のフレーム番号と総フレーム数を比較して、終了タイミングを検知させる
詳細設計
cached_network_image からcached_network_image_provider.dartと_image_loader.dartを持ってきます。
cached_network_image_provider.dartはgif用に以下のような変更を加えます
int? _frameCount;
int? get frameCount => _frameCount;
Stream<ui.Codec> _loadImageAsync(
CachedNetworkGifProvider key,
StreamController<ImageChunkEvent> chunkEvents,
ImageDecoderCallback decode,
) {
assert(key == this);
final codec = ImageLoader()
.loadImageAsync(
url,
cacheKey,
chunkEvents,
decode,
cacheManager ?? defaultCacheManager,
maxHeight,
maxWidth,
headers,
imageRenderMethodForWeb,
() => PaintingBinding.instance.imageCache.evict(key),
)
.asBroadcastStream()
..listen((event) {
_frameCount = event.frameCount;
});
return codec;
}
続けてキャッシュ機能を利用しつつ、Gifをループさせず表示させるためのクラスを作成します
/// 一度だけ再生するGif
class CachedGif extends StatelessWidget {
CachedGif(
this.path, {
this.width,
this.height,
this.color,
this.fit,
this.onFinish,
super.key,
});
final String path;
final double? width;
final double? height;
final BoxFit? fit;
final Color? color;
bool isFinished = false;
final void Function()? onFinish;
@override
Widget build(BuildContext context) {
final _cachedNetworkGifProvider = CachedNetworkGifProvider(path);
return Image(
image: _cachedNetworkGifProvider,
width: width,
height: height,
fit: fit,
color: color,
frameBuilder: (context, child, frame, wasSynchronouslyLoaded) {
final frameCount = _cachedNetworkGifProvider.frameCount;
if (frame != null && frameCount != null && frame >= frameCount - 1) {
if (!isFinished) {
onFinish?.call();
isFinished = true;
}
return const SizedBox.shrink();
}
return child;
},
errorBuilder: (context, error, stackTrace) {
return const Icon(Icons.error);
},
);
}
}
コード全体はhttps://github.com/yamacrypt/Cached-Gif に乗せています
最後に
CachedNetworkImageProviderを使いながらGifを制御することに関する有益な情報が見つからなくて、実装アイデアを出すまでが大変だったので記事にしてみました。
誰かの役にたてば幸いです。
後もっといい実装アイデアあれば教えてほしいです。
Discussion