208 lines
5.1 KiB
Dart
208 lines
5.1 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_svga/flutter_svga.dart';
|
||
import 'package:yumi/ui_kit/components/sc_tts.dart';
|
||
|
||
import 'package:yumi/shared/tools/sc_gift_vap_svga_manager.dart';
|
||
|
||
class SVGAHeadwearWidget extends StatefulWidget {
|
||
// 资源路径(网络URL或本地assets路径)
|
||
final String resource;
|
||
|
||
// 尺寸
|
||
final double? width;
|
||
final double? height;
|
||
|
||
// 动画控制参数
|
||
final bool autoPlay;
|
||
final int loops; // 循环次数(0表示无限循环)
|
||
final bool clearsAfterStop;
|
||
|
||
// 缓存控制
|
||
final bool useCache;
|
||
|
||
// 回调函数
|
||
final VoidCallback? onCompleted;
|
||
final Function(String)? onError;
|
||
final VoidCallback? onStartLoading;
|
||
final VoidCallback? onFinishLoading;
|
||
|
||
const SVGAHeadwearWidget({
|
||
Key? key,
|
||
required this.resource,
|
||
this.width,
|
||
this.height,
|
||
this.autoPlay = true,
|
||
this.loops = 0,
|
||
this.clearsAfterStop = true,
|
||
this.useCache = true,
|
||
this.onCompleted,
|
||
this.onError,
|
||
this.onStartLoading,
|
||
this.onFinishLoading,
|
||
}) : super(key: key);
|
||
|
||
@override
|
||
_SVGAHeadwearWidgetState createState() => _SVGAHeadwearWidgetState();
|
||
}
|
||
|
||
class _SVGAHeadwearWidgetState extends State<SVGAHeadwearWidget>
|
||
with SingleTickerProviderStateMixin {
|
||
SVGAAnimationController? _animationController;
|
||
bool _isLoading = true;
|
||
bool _isNetworkResource = false;
|
||
bool _hasError = false;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_isNetworkResource = widget.resource.startsWith('http');
|
||
_animationController = SVGAAnimationController(vsync: this);
|
||
|
||
if (widget.autoPlay) {
|
||
_loadAnimation();
|
||
}
|
||
|
||
// 添加动画状态监听
|
||
_animationController?.addStatusListener((status) {
|
||
if (status == AnimationStatus.completed && widget.onCompleted != null) {
|
||
widget.onCompleted!();
|
||
}
|
||
});
|
||
}
|
||
|
||
// 加载动画(带缓存)
|
||
Future<void> _loadAnimation() async {
|
||
if (widget.onStartLoading != null) {
|
||
widget.onStartLoading!();
|
||
}
|
||
|
||
setState(() {
|
||
_isLoading = true;
|
||
_hasError = false;
|
||
});
|
||
|
||
try {
|
||
// 检查缓存
|
||
MovieEntity? videoItem;
|
||
if (widget.useCache) {
|
||
videoItem = SCGiftVapSvgaManager().videoItemCache[widget.resource];
|
||
}
|
||
|
||
// 如果没有缓存,则进行解析
|
||
if (videoItem == null) {
|
||
videoItem =
|
||
_isNetworkResource
|
||
? await SVGAParser.shared.decodeFromURL(widget.resource)
|
||
: await SVGAParser.shared.decodeFromAssets(widget.resource);
|
||
videoItem.autorelease =false;
|
||
// 存入缓存
|
||
if (widget.useCache) {
|
||
SCGiftVapSvgaManager().videoItemCache[widget.resource] = videoItem;
|
||
}
|
||
}
|
||
|
||
if (mounted) {
|
||
setState(() {
|
||
_animationController?.videoItem = videoItem;
|
||
_isLoading = false;
|
||
});
|
||
|
||
// 根据循环次数设置播放方式
|
||
if (widget.loops == 0) {
|
||
_animationController?.repeat();
|
||
} else {
|
||
_animationController?.repeat(count: widget.loops);
|
||
}
|
||
|
||
if (widget.onFinishLoading != null) {
|
||
widget.onFinishLoading!();
|
||
}
|
||
}
|
||
} catch (e) {
|
||
if (mounted) {
|
||
setState(() {
|
||
_isLoading = false;
|
||
_hasError = true;
|
||
});
|
||
}
|
||
|
||
if (widget.onError != null) {
|
||
SCTts.show("fail-${e.toString()}");
|
||
widget.onError!(e.toString());
|
||
}
|
||
}
|
||
}
|
||
|
||
// 播放动画
|
||
void play() {
|
||
if (_animationController?.videoItem != null) {
|
||
_animationController?.reset();
|
||
if (widget.loops == 0) {
|
||
_animationController?.repeat();
|
||
} else {
|
||
_animationController?.repeat(count: widget.loops);
|
||
}
|
||
} else {
|
||
_loadAnimation();
|
||
}
|
||
}
|
||
|
||
// 暂停动画
|
||
void pause() {
|
||
_animationController?.stop();
|
||
}
|
||
|
||
// 停止动画
|
||
void stop() {
|
||
_animationController?.stop();
|
||
if (widget.clearsAfterStop) {
|
||
_animationController?.videoItem = null;
|
||
}
|
||
}
|
||
|
||
// 重新加载动画
|
||
void reload() {
|
||
if (widget.useCache) {
|
||
// 清除缓存项
|
||
SCGiftVapSvgaManager().videoItemCache.remove(widget.resource);
|
||
}
|
||
_loadAnimation();
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
stop();
|
||
_animationController?.dispose();
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
if (_isLoading && !widget.autoPlay) {
|
||
return Container(
|
||
width: widget.width,
|
||
height: widget.height,
|
||
child: Center(child: CircularProgressIndicator()),
|
||
);
|
||
}
|
||
|
||
if (_hasError) {
|
||
return Container();
|
||
}
|
||
|
||
return Container(
|
||
width: widget.width,
|
||
height: widget.height,
|
||
child:
|
||
_animationController != null &&
|
||
_animationController!.videoItem != null
|
||
? SVGAImage(
|
||
_animationController!,
|
||
fit: BoxFit.fill,
|
||
clearsAfterStop: widget.clearsAfterStop,
|
||
)
|
||
: Container(),
|
||
);
|
||
}
|
||
}
|