yumi-flutter/lib/ui_kit/widgets/headdress/headdress_widget.dart
2026-07-07 23:16:12 +08:00

242 lines
6.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
final Set<String> _retainedSvgaResources = <String>{};
bool _isLoading = true;
bool _hasError = false;
@override
void initState() {
super.initState();
_animationController = SVGAAnimationController(vsync: this);
if (widget.autoPlay) {
_loadAnimation();
}
// 添加动画状态监听
_animationController?.addStatusListener((status) {
if (status == AnimationStatus.completed && widget.onCompleted != null) {
widget.onCompleted!();
}
});
}
@override
void didUpdateWidget(covariant SVGAHeadwearWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.resource != widget.resource ||
oldWidget.useCache != widget.useCache) {
_loadAnimation();
}
}
// 加载动画(带缓存)
Future<void> _loadAnimation() async {
if (widget.onStartLoading != null) {
widget.onStartLoading!();
}
setState(() {
_isLoading = true;
_hasError = false;
});
final resource = widget.resource.trim();
var retainedForLoad = false;
try {
retainedForLoad = _retainSvgaResourceIfNeeded(resource);
final videoItem = await SCGiftVapSvgaManager().loadSvgaEntity(
resource,
retain: retainedForLoad,
);
if (mounted && widget.resource.trim() == resource) {
setState(() {
_animationController?.videoItem = videoItem;
_isLoading = false;
});
_releaseRetainedSvgaResourcesExcept(resource);
// 根据循环次数设置播放方式
if (widget.loops == 0) {
_animationController?.repeat();
} else {
_animationController?.repeat(count: widget.loops);
}
if (widget.onFinishLoading != null) {
widget.onFinishLoading!();
}
} else if (retainedForLoad) {
_releaseSvgaResource(resource);
}
} catch (e) {
if (retainedForLoad) {
_releaseSvgaResource(resource);
}
if (mounted) {
setState(() {
_isLoading = false;
_hasError = true;
});
}
if (widget.onError != null) {
SCTts.show("fail-${e.toString()}");
widget.onError!(e.toString());
}
}
}
bool _retainSvgaResourceIfNeeded(String resource) {
if (_retainedSvgaResources.contains(resource)) {
return false;
}
_retainedSvgaResources.add(resource);
return true;
}
void _releaseSvgaResource(String resource, {bool evict = false}) {
final shouldRelease = _retainedSvgaResources.remove(resource);
if (shouldRelease || evict) {
SCGiftVapSvgaManager().removeSvgaEntity(resource, evict: evict);
}
}
void _releaseRetainedSvgaResourcesExcept(
String? activeResource, {
bool evict = false,
}) {
final resources = _retainedSvgaResources.toList(growable: false);
for (final resource in resources) {
if (resource != activeResource) {
_releaseSvgaResource(resource, evict: evict);
}
}
}
// 播放动画
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) {
final resource = widget.resource.trim();
_animationController?.videoItem = null;
_releaseSvgaResource(resource, evict: !widget.useCache);
}
}
// 重新加载动画
void reload() {
final resource = widget.resource.trim();
_animationController?.stop();
_animationController?.videoItem = null;
_releaseSvgaResource(resource, evict: true);
_loadAnimation();
}
@override
void dispose() {
_animationController?.stop();
_animationController?.videoItem = null;
_releaseRetainedSvgaResourcesExcept(null, evict: !widget.useCache);
_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(),
);
}
}