|
|
|
@ -13,7 +13,7 @@ class SvgaPlayerWidget extends StatefulWidget { |
|
|
|
} |
|
|
|
|
|
|
|
class _SvgaPlayerWidgetState extends State<SvgaPlayerWidget> |
|
|
|
with SingleTickerProviderStateMixin { |
|
|
|
with TickerProviderStateMixin { |
|
|
|
final SvgaPlayerManager _manager = SvgaPlayerManager.instance; |
|
|
|
SVGAAnimationController? _controller; |
|
|
|
SvgaAnimationItem? _currentItem; |
|
|
|
@ -23,8 +23,20 @@ class _SvgaPlayerWidgetState extends State<SvgaPlayerWidget> |
|
|
|
super.initState(); |
|
|
|
// 监听队列变化 |
|
|
|
ever(_manager.currentItem, (item) { |
|
|
|
if (item != null && item != _currentItem) { |
|
|
|
_playAnimation(item); |
|
|
|
print( |
|
|
|
'📢 currentItem 变化: ${item?.svgaFile ?? "null"}, 当前 _currentItem: ${_currentItem?.svgaFile ?? "null"}', |
|
|
|
); |
|
|
|
if (item != null) { |
|
|
|
// 如果当前没有播放,或者新的 item 与当前不同,则播放 |
|
|
|
if (_currentItem == null || item.svgaFile != _currentItem!.svgaFile) { |
|
|
|
print('🎯 准备播放新动画: ${item.svgaFile}'); |
|
|
|
_playAnimation(item); |
|
|
|
} else { |
|
|
|
print('⚠️ 相同的动画,跳过播放'); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// currentItem 变为 null,但不立即清理,等待播放完成回调 |
|
|
|
print('📢 currentItem 变为 null,等待播放完成回调'); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
@ -37,48 +49,162 @@ class _SvgaPlayerWidgetState extends State<SvgaPlayerWidget> |
|
|
|
|
|
|
|
/// 播放动画 |
|
|
|
Future<void> _playAnimation(SvgaAnimationItem item) async { |
|
|
|
// 如果正在播放,先停止 |
|
|
|
print( |
|
|
|
'🎬 开始播放动画: ${item.svgaFile}, 当前状态: _controller=${_controller != null}, _currentItem=${_currentItem?.svgaFile ?? "null"}', |
|
|
|
); |
|
|
|
|
|
|
|
// 如果正在播放,先停止并清理 |
|
|
|
if (_controller != null) { |
|
|
|
print('🛑 停止当前播放'); |
|
|
|
_controller!.stop(); |
|
|
|
_controller!.dispose(); |
|
|
|
_controller = null; |
|
|
|
} |
|
|
|
|
|
|
|
// 设置当前项并创建新的 controller |
|
|
|
_currentItem = item; |
|
|
|
_controller = SVGAAnimationController(vsync: this); |
|
|
|
print('✅ 创建新的 controller,准备加载动画'); |
|
|
|
|
|
|
|
try { |
|
|
|
// 判断是网络 URL 还是本地资源 |
|
|
|
if (item.svgaFile.startsWith('http://') || |
|
|
|
item.svgaFile.startsWith('https://')) { |
|
|
|
// 网络 URL |
|
|
|
SVGAParser.shared.decodeFromURL(item.svgaFile).then((video) { |
|
|
|
if (mounted && _currentItem == item) { |
|
|
|
_controller!.videoItem = video; |
|
|
|
_controller!.repeat(); |
|
|
|
print('✅ SVGA 动画加载成功(网络): ${item.svgaFile}'); |
|
|
|
} |
|
|
|
}).catchError((error) { |
|
|
|
print('❌ SVGA 动画加载失败(网络): $error'); |
|
|
|
_manager.onAnimationError(error.toString()); |
|
|
|
}); |
|
|
|
SVGAParser.shared |
|
|
|
.decodeFromURL(item.svgaFile) |
|
|
|
.then((video) { |
|
|
|
if (!mounted) return; |
|
|
|
|
|
|
|
// 检查是否还是当前要播放的动画 |
|
|
|
if (_currentItem != item || _controller == null) { |
|
|
|
print('⚠️ 动画已变更,取消播放: ${item.svgaFile}'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
_controller!.videoItem = video; |
|
|
|
// 播放动画(repeat 会循环播放,我们需要监听完成) |
|
|
|
_controller!.repeat(); |
|
|
|
|
|
|
|
// 获取动画时长,如果为 null 则使用默认值 3 秒 |
|
|
|
final duration = _controller!.duration; |
|
|
|
final playDuration = duration != null && duration > Duration.zero |
|
|
|
? duration |
|
|
|
: const Duration(seconds: 3); |
|
|
|
|
|
|
|
print( |
|
|
|
'✅ SVGA 动画加载成功(网络): ${item.svgaFile}, 播放时长: ${playDuration.inMilliseconds}ms', |
|
|
|
); |
|
|
|
|
|
|
|
// 在动画时长后停止并通知完成 |
|
|
|
Future.delayed(playDuration, () { |
|
|
|
if (!mounted) { |
|
|
|
print('⚠️ Widget 已卸载,取消完成回调'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 再次检查是否还是当前动画 |
|
|
|
if (_currentItem == item && _controller != null) { |
|
|
|
print('✅ SVGA 动画播放完成(网络): ${item.svgaFile}'); |
|
|
|
|
|
|
|
// 先停止动画 |
|
|
|
_controller!.stop(); |
|
|
|
|
|
|
|
// 清理当前项和 controller |
|
|
|
final wasCurrentItem = _currentItem; |
|
|
|
_currentItem = null; |
|
|
|
_controller?.dispose(); |
|
|
|
_controller = null; |
|
|
|
|
|
|
|
// 通知管理器播放完成(这会触发下一个动画) |
|
|
|
if (wasCurrentItem == item) { |
|
|
|
_manager.onAnimationFinished(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
print( |
|
|
|
'⚠️ 动画已变更,跳过完成回调: _currentItem=${_currentItem?.svgaFile ?? "null"}, item=${item.svgaFile}', |
|
|
|
); |
|
|
|
} |
|
|
|
}); |
|
|
|
}) |
|
|
|
.catchError((error) { |
|
|
|
print('❌ SVGA 动画加载失败(网络): $error'); |
|
|
|
_currentItem = null; |
|
|
|
_controller?.dispose(); |
|
|
|
_controller = null; |
|
|
|
_manager.onAnimationError(error.toString()); |
|
|
|
}); |
|
|
|
} else { |
|
|
|
// 本地资源(assets) |
|
|
|
SVGAParser.shared.decodeFromAssets(item.svgaFile).then((video) { |
|
|
|
if (mounted && _currentItem == item) { |
|
|
|
_controller!.videoItem = video; |
|
|
|
_controller!.repeat(); |
|
|
|
print('✅ SVGA 动画加载成功(本地): ${item.svgaFile}'); |
|
|
|
} |
|
|
|
}).catchError((error) { |
|
|
|
print('❌ SVGA 动画加载失败(本地): $error'); |
|
|
|
_manager.onAnimationError(error.toString()); |
|
|
|
}); |
|
|
|
} |
|
|
|
SVGAParser.shared |
|
|
|
.decodeFromAssets(item.svgaFile) |
|
|
|
.then((video) { |
|
|
|
if (!mounted) return; |
|
|
|
|
|
|
|
// 检查是否还是当前要播放的动画 |
|
|
|
if (_currentItem != item || _controller == null) { |
|
|
|
print('⚠️ 动画已变更,取消播放: ${item.svgaFile}'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
_controller!.videoItem = video; |
|
|
|
// 播放动画(repeat 会循环播放,我们需要监听完成) |
|
|
|
_controller!.repeat(); |
|
|
|
|
|
|
|
// 获取动画时长,如果为 null 则使用默认值 3 秒 |
|
|
|
final duration = _controller!.duration; |
|
|
|
final playDuration = duration != null && duration > Duration.zero |
|
|
|
? duration |
|
|
|
: const Duration(seconds: 3); |
|
|
|
|
|
|
|
print( |
|
|
|
'✅ SVGA 动画加载成功(本地): ${item.svgaFile}, 播放时长: ${playDuration.inMilliseconds}ms', |
|
|
|
); |
|
|
|
|
|
|
|
// 在动画时长后停止并通知完成 |
|
|
|
Future.delayed(playDuration, () { |
|
|
|
if (!mounted) { |
|
|
|
print('⚠️ Widget 已卸载,取消完成回调'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 监听动画完成(repeat 模式不会自动完成,需要手动停止) |
|
|
|
// 这里可以根据需要调整播放逻辑 |
|
|
|
// 再次检查是否还是当前动画 |
|
|
|
if (_currentItem == item && _controller != null) { |
|
|
|
print('✅ SVGA 动画播放完成(本地): ${item.svgaFile}'); |
|
|
|
|
|
|
|
// 先停止动画 |
|
|
|
_controller!.stop(); |
|
|
|
|
|
|
|
// 清理当前项和 controller |
|
|
|
final wasCurrentItem = _currentItem; |
|
|
|
_currentItem = null; |
|
|
|
_controller?.dispose(); |
|
|
|
_controller = null; |
|
|
|
|
|
|
|
// 通知管理器播放完成(这会触发下一个动画) |
|
|
|
if (wasCurrentItem == item) { |
|
|
|
_manager.onAnimationFinished(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
print( |
|
|
|
'⚠️ 动画已变更,跳过完成回调: _currentItem=${_currentItem?.svgaFile ?? "null"}, item=${item.svgaFile}', |
|
|
|
); |
|
|
|
} |
|
|
|
}); |
|
|
|
}) |
|
|
|
.catchError((error) { |
|
|
|
print('❌ SVGA 动画加载失败(本地): $error'); |
|
|
|
_currentItem = null; |
|
|
|
_controller?.dispose(); |
|
|
|
_controller = null; |
|
|
|
_manager.onAnimationError(error.toString()); |
|
|
|
}); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
print('❌ SVGA 播放异常: $e'); |
|
|
|
_currentItem = null; |
|
|
|
_controller?.dispose(); |
|
|
|
_controller = null; |
|
|
|
_manager.onAnimationError(e.toString()); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -94,11 +220,8 @@ class _SvgaPlayerWidgetState extends State<SvgaPlayerWidget> |
|
|
|
} |
|
|
|
|
|
|
|
return Positioned.fill( |
|
|
|
child: IgnorePointer( |
|
|
|
child: SVGAImage(_controller!), |
|
|
|
), |
|
|
|
child: IgnorePointer(child: SVGAImage(_controller!)), |
|
|
|
); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|