Browse Source

feat(discover): 实现SVGA动画播放管理器和播放组件

- 新增 SvgaPlayerManager 管理SVGA动画播放队列
- 创建 SvgaPlayerWidget 用于实际播放SVGA动画
- 支持本地assets和网络URL两种SVGA文件加载方式
- 实现动画播放完成和错误处理回调机制
- 提供队列控制方法如添加、清空、停止播放等功能
- 修复房间控制器中SVGA播放器导入路径错误问题
ios
Jolie 4 months ago
parent
commit
d6e85c1220
3 changed files with 191 additions and 1 deletions
  1. 2
      lib/controller/discover/room_controller.dart
  2. 86
      lib/controller/discover/svga_player_manager.dart
  3. 104
      lib/widget/live/svga_player_widget.dart

2
lib/controller/discover/room_controller.dart

@ -2,7 +2,7 @@ import 'dart:convert';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:dating_touchme_app/controller/global.dart';
import 'package:dating_touchme_app/controller/live/svga_player_manager.dart';
import 'package:dating_touchme_app/controller/discover/svga_player_manager.dart';
import 'package:dating_touchme_app/model/live/gift_product_model.dart';
import 'package:dating_touchme_app/model/rtc/rtc_channel_data.dart';
import 'package:dating_touchme_app/model/rtc/rtc_channel_detail.dart';

86
lib/controller/discover/svga_player_manager.dart

@ -0,0 +1,86 @@
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:flutter_svga/flutter_svga.dart';
import 'package:get/get.dart';
/// SVGA
class SvgaAnimationItem {
final String svgaFile;
final String? targetUserId; // ID
final String? senderUserId; // ID
final String? giftProductId; // ID
SvgaAnimationItem({
required this.svgaFile,
this.targetUserId,
this.senderUserId,
this.giftProductId,
});
}
/// SVGA
/// SVGAAnimationController vsync Widget
/// Widget
class SvgaPlayerManager extends GetxController {
static SvgaPlayerManager? _instance;
static SvgaPlayerManager get instance {
_instance ??= Get.put(SvgaPlayerManager());
return _instance!;
}
final Queue<SvgaAnimationItem> _animationQueue = Queue<SvgaAnimationItem>();
final Rx<SvgaAnimationItem?> currentItem = Rx<SvgaAnimationItem?>(null);
final RxBool isPlaying = false.obs;
///
void addToQueue(SvgaAnimationItem item) {
_animationQueue.add(item);
_playNext();
}
///
void _playNext() {
if (isPlaying.value || _animationQueue.isEmpty) {
return;
}
final item = _animationQueue.removeFirst();
currentItem.value = item;
isPlaying.value = true;
print('✅ SVGA 动画已添加到播放队列: ${item.svgaFile}');
}
///
void onAnimationFinished() {
print('✅ SVGA 动画播放完成');
isPlaying.value = false;
currentItem.value = null;
//
_playNext();
}
///
void onAnimationError(String error) {
print('❌ SVGA 动画播放失败: $error');
isPlaying.value = false;
currentItem.value = null;
//
_playNext();
}
///
void stop() {
isPlaying.value = false;
currentItem.value = null;
}
///
void clearQueue() {
stop();
_animationQueue.clear();
}
///
int get queueLength => _animationQueue.length;
}

104
lib/widget/live/svga_player_widget.dart

@ -0,0 +1,104 @@
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 SingleTickerProviderStateMixin {
final SvgaPlayerManager _manager = SvgaPlayerManager.instance;
SVGAAnimationController? _controller;
SvgaAnimationItem? _currentItem;
@override
void initState() {
super.initState();
//
ever(_manager.currentItem, (item) {
if (item != null && item != _currentItem) {
_playAnimation(item);
}
});
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
///
Future<void> _playAnimation(SvgaAnimationItem item) async {
//
if (_controller != null) {
_controller!.dispose();
_controller = null;
}
_currentItem = item;
_controller = SVGAAnimationController(vsync: this);
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());
});
} 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());
});
}
// repeat
//
} catch (e) {
print('❌ SVGA 播放异常: $e');
_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!),
),
);
});
}
}
Loading…
Cancel
Save