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.
 
 
 
 
 

228 lines
8.0 KiB

import 'package:flutter/material.dart';
import 'package:flutter_svga/flutter_svga.dart';
import 'package:get/get.dart';
import 'package:dating_touchme_app/controller/discover/svga_player_manager.dart';
/// SVGA 动画播放 Widget
/// 监听 SvgaPlayerManager 的队列,自动播放 SVGA 动画
class SvgaPlayerWidget extends StatefulWidget {
const SvgaPlayerWidget({super.key});
@override
State<SvgaPlayerWidget> createState() => _SvgaPlayerWidgetState();
}
class _SvgaPlayerWidgetState extends State<SvgaPlayerWidget>
with TickerProviderStateMixin {
final SvgaPlayerManager _manager = SvgaPlayerManager.instance;
SVGAAnimationController? _controller;
SvgaAnimationItem? _currentItem;
@override
void initState() {
super.initState();
// 监听队列变化
ever(_manager.currentItem, (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,等待播放完成回调');
}
});
}
@override
void dispose() {
_controller?.dispose();
_manager.clearQueue();
super.dispose();
}
/// 播放动画
Future<void> _playAnimation(SvgaAnimationItem item) async {
print(
'🎬 开始播放动画: ${item.svgaFile}, 当前状态: _controller=${_controller != null}, _currentItem=${_currentItem?.svgaFile ?? "null"}',
);
// 如果正在播放,先停止并清理
if (_controller != null) {
print('🛑 停止当前播放');
_controller!.dispose();
_controller!.stop();
_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) 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) 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());
});
}
} catch (e) {
print('❌ SVGA 播放异常: $e');
_currentItem = null;
_controller?.dispose();
_controller = null;
_manager.onAnimationError(e.toString());
}
}
@override
Widget build(BuildContext context) {
return Obx(() {
final currentItem = _manager.currentItem.value;
final isPlaying = _manager.isPlaying.value;
if (!isPlaying || currentItem == null || _controller == null) {
return const SizedBox.shrink();
}
return Positioned.fill(
child: IgnorePointer(child: SVGAImage(_controller!)),
);
});
}
}