You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
flutter_gifimage/lib/flutter_gifimage.dart

252 lines
6.2 KiB

5 years ago
/*
author: Jpeng
email: peng8350@gmail.com
time:2019-7-26 3:30
*/
library flutter_gifimage;
import 'dart:io';
import 'dart:ui' as ui show Codec;
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
5 years ago
/// cache gif fetched image
5 years ago
class GifCache{
3 years ago
final Map<String,List<ImageInfo>?> caches= Map();
5 years ago
5 years ago
void clear() {
caches.clear();
}
bool evict(Object key) {
3 years ago
final List<ImageInfo>? pendingImage = caches.remove(key);
5 years ago
if(pendingImage!=null){
return true;
}
return false;
}
}
5 years ago
5 years ago
/// controll gif
class GifController extends AnimationController{
GifController({
3 years ago
required TickerProvider vsync,
5 years ago
double value=0.0,
3 years ago
Duration? reverseDuration,
Duration? duration,
AnimationBehavior? animationBehavior
5 years ago
}):super.unbounded(
value:value,
reverseDuration:reverseDuration,
duration:duration,
animationBehavior:animationBehavior??AnimationBehavior.normal,
vsync:vsync);
5 years ago
@override
void reset() {
// TODO: implement reset
value = 0.0;
}
5 years ago
}
5 years ago
5 years ago
class GifImage extends StatefulWidget{
GifImage({
3 years ago
required this.image,
required this.controller,
5 years ago
this.semanticLabel,
this.excludeFromSemantics = false,
this.width,
this.height,
this.onFetchCompleted,
this.color,
this.colorBlendMode,
this.fit,
this.alignment = Alignment.center,
this.repeat = ImageRepeat.noRepeat,
this.centerSlice,
this.matchTextDirection = false,
this.gaplessPlayback = false,
});
3 years ago
final VoidCallback? onFetchCompleted;
5 years ago
final GifController controller;
final ImageProvider image;
3 years ago
final double? width;
final double? height;
final Color? color;
final BlendMode? colorBlendMode;
final BoxFit? fit;
5 years ago
final AlignmentGeometry alignment;
final ImageRepeat repeat;
3 years ago
final Rect? centerSlice;
5 years ago
final bool matchTextDirection;
final bool gaplessPlayback;
3 years ago
final String? semanticLabel;
5 years ago
final bool excludeFromSemantics;
@override
State<StatefulWidget> createState() {
return new GifImageState();
}
5 years ago
static GifCache cache = GifCache();
5 years ago
}
class GifImageState extends State<GifImage>{
3 years ago
List<ImageInfo>? _infos;
5 years ago
int _curIndex = 0;
bool _fetchComplete= false;
3 years ago
ImageInfo? get _imageInfo {
if(!_fetchComplete)return null;
3 years ago
return _infos==null?null:_infos![_curIndex];
}
5 years ago
@override
void initState() {
super.initState();
widget.controller.addListener(_listener);
}
@override
void dispose() {
super.dispose();
widget.controller.removeListener(_listener);
}
@override
void didUpdateWidget(GifImage oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.image != oldWidget.image) {
5 years ago
fetchGif(widget.image).then((imageInfors){
if(mounted)
setState(() {
_infos = imageInfors;
_fetchComplete=true;
_curIndex = widget.controller.value.toInt();
if(widget.onFetchCompleted!=null){
3 years ago
widget.onFetchCompleted!();
}
});
5 years ago
});
5 years ago
}
if (widget.controller != oldWidget.controller) {
oldWidget.controller.removeListener(_listener);
widget.controller.addListener(_listener);
}
}
void _listener(){
if(_curIndex!=widget.controller.value&&_fetchComplete){
if(mounted)
setState(() {
_curIndex = widget.controller.value.toInt();
});
5 years ago
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
if(_infos==null){
5 years ago
fetchGif(widget.image).then((imageInfors){
if(mounted)
setState(() {
_infos = imageInfors;
_fetchComplete=true;
_curIndex = widget.controller.value.toInt();
if(widget.onFetchCompleted!=null){
3 years ago
widget.onFetchCompleted!();
}
});
5 years ago
});
5 years ago
}
}
@override
Widget build(BuildContext context) {
final RawImage image = new RawImage(
image: _imageInfo?.image,
width: widget.width,
height: widget.height,
scale: _imageInfo?.scale ?? 1.0,
color: widget.color,
colorBlendMode: widget.colorBlendMode,
fit: widget.fit,
alignment: widget.alignment,
repeat: widget.repeat,
centerSlice: widget.centerSlice,
matchTextDirection: widget.matchTextDirection,
);
if (widget.excludeFromSemantics)
return image;
return new Semantics(
container: widget.semanticLabel != null,
image: true,
label: widget.semanticLabel == null ? '' : widget.semanticLabel,
child: image,
);
}
}
5 years ago
final HttpClient _sharedHttpClient = HttpClient()..autoUncompress = false;
HttpClient get _httpClient {
HttpClient client = _sharedHttpClient;
assert(() {
if (debugNetworkImageHttpClientProvider != null)
3 years ago
client = debugNetworkImageHttpClientProvider!();
5 years ago
return true;
}());
return client;
}
3 years ago
Future<List<ImageInfo>?> fetchGif(ImageProvider provider) async{
List<ImageInfo>? infos = [];
late dynamic data;
5 years ago
String key =provider is NetworkImage?provider.url:provider is AssetImage?provider.assetName:provider is MemoryImage?provider.bytes.toString():"";
if(GifImage.cache.caches.containsKey(key)){
infos = GifImage.cache.caches[key];
return infos;
}
if(provider is NetworkImage){
final Uri resolved = Uri.base.resolve(provider.url);
final HttpClientRequest request = await _httpClient.getUrl(resolved);
provider.headers?.forEach((String name, String value) {
request.headers.add(name, value);
});
final HttpClientResponse response = await request.close();
data = await consolidateHttpClientResponseBytes(
response,
);
}
else if(provider is AssetImage){
AssetBundleImageKey key = await provider.obtainKey(ImageConfiguration());
data = await key.bundle.load(key.name);
}
else if(provider is FileImage){
data = await provider.file.readAsBytes();
}
else if(provider is MemoryImage){
data = provider.bytes;
}
3 years ago
ui.Codec codec=await PaintingBinding.instance!.instantiateImageCodec(data.buffer.asUint8List());
5 years ago
infos = [];
for(int i = 0;i<codec.frameCount;i++){
FrameInfo frameInfo = await codec.getNextFrame();
//scale ??
infos.add(ImageInfo(image: frameInfo.image));
}
GifImage.cache.caches.putIfAbsent(key, () => infos);
return infos;
}