import 'dart:ui'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:yumi/ui_kit/components/sc_compontent.dart'; import 'package:yumi/ui_kit/components/text/sc_text.dart'; import 'package:yumi/ui_kit/theme/socialchat_theme.dart'; import 'package:yumi/app/constants/sc_room_msg_type.dart'; import 'package:provider/provider.dart'; import 'package:yumi/app/constants/sc_screen.dart'; import 'package:yumi/shared/tools/sc_path_utils.dart'; import 'package:yumi/shared/business_logic/models/res/join_room_res.dart'; import 'package:yumi/shared/business_logic/models/res/mic_res.dart'; import 'package:yumi/services/audio/rtc_manager.dart'; import '../../../shared/data_sources/models/enum/sc_room_special_mike_type.dart'; ///麦位 class SCSeatItem extends StatefulWidget { final num index; final bool isGameModel; const SCSeatItem({Key? key, required this.index, this.isGameModel = false}) : super(key: key); @override _SCSeatItemState createState() => _SCSeatItemState(); } class _SCSeatItemState extends State with TickerProviderStateMixin { RtcProvider? provider; JoinRoomRes? room; MicRes? roomSeat; final GlobalKey _targetKey = GlobalKey(); @override void initState() { super.initState(); provider = Provider.of(context, listen: false); provider?.bindTargetKey(widget.index, _targetKey); } @override Widget build(BuildContext context) { roomSeat = provider?.roomWheatMap[widget.index]; room = provider?.currenRoom; return GestureDetector( behavior: HitTestBehavior.opaque, child: Column( children: [ SizedBox( key: _targetKey, width: widget.isGameModel ? 40.w : 55.w, height: widget.isGameModel ? 40.w : 55.w, child: Stack( alignment: Alignment.center, children: [ Sonic( index: widget.index, room: room, isGameModel: widget.isGameModel, ), roomSeat?.user != null ? head( url: roomSeat?.user?.userAvatar ?? "", width: widget.isGameModel ? 40.w : 55.w, headdress: SCPathUtils.getFileExtension( roomSeat?.user ?.getHeaddress() ?.sourceUrl ?? "", ).toLowerCase() == ".mp4" && window.locale.languageCode == "ar" ? "" : roomSeat?.user?.getHeaddress()?.sourceUrl, ) : (roomSeat!.micLock! ? Image.asset( "sc_images/room/sc_icon_seat_lock.png", width: widget.isGameModel ? 38.w : 52.w, height: widget.isGameModel ? 38.w : 52.w, ) : Image.asset( "sc_images/room/sc_icon_seat_open.png", width: widget.isGameModel ? 38.w : 52.w, height: widget.isGameModel ? 38.w : 52.w, )), Positioned( bottom: widget.isGameModel ? 2.w : 5.w, right: widget.isGameModel ? 2.w : 5.w, child: roomSeat!.micMute! ? Image.asset( "sc_images/room/sc_icon_room_seat_mic_mute.png", width: 14.w, height: 14.w, ) : Container(), ), IgnorePointer( child: Emoticons( index: widget.index, isGameModel: widget.isGameModel, ), ), ], ), ), widget.isGameModel ? Container() : (roomSeat?.user != null ? Container( width: 64.w, child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ // 角色标签 msgRoleTag( roomSeat?.user?.roles ?? "", width: 15.w, height: 15.w, ), // 用户名文本 Flexible( child: socialchatNickNameText( fontWeight: FontWeight.w600, roomSeat?.user?.userNickname ?? "", fontSize: 10.sp, type: roomSeat?.user?.getVIP()?.name ?? "", needScroll: (roomSeat ?.user ?.userNickname ?.characters .length ?? 0) > 8, ), ), ], ), ) : Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: 16.w), text( "NO.${widget.index}", fontSize: 10.sp, fontWeight: FontWeight.w600, ), ], )), widget.isGameModel ? Container() : (room?.roomProfile?.roomSetting?.showHeartbeat == true ? Container( padding: EdgeInsets.symmetric( vertical: 1.w, horizontal: 5.w, ), margin: EdgeInsets.symmetric(horizontal: 5.w), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image.asset( "sc_images/room/sc_icon_gift_heartbeat.png", width: 13.w, ), SizedBox(width: 3.w), text( _heartbeatVaFormat(), fontWeight: FontWeight.w600, fontSize: 10.sp, ), ], ), ) : Container()), ], ), onTap: () { Provider.of( context, listen: false, ).clickSite(widget.index); }, ); } String _heartbeatVaFormat() { int value = (roomSeat?.user?.heartbeatVal ?? 0).toInt(); if (value > 99999) { return "${(value / 1000).toStringAsFixed(0)}k"; } return "$value"; } } class Emoticons extends StatefulWidget { final num index; final bool isGameModel; const Emoticons({Key? key, required this.index, this.isGameModel = false}) : super(key: key); @override _EmoticonsState createState() => _EmoticonsState(); } class _EmoticonsState extends State with TickerProviderStateMixin { late AnimationController emoticonsAniCtr; late CurvedAnimation curvedAnimation; bool showIn = false; MicRes? playingRes; List pathList = []; String? giftPath; List giftList = []; bool showResult = false; @override void initState() { // TODO: implement initState emoticonsAniCtr = AnimationController( vsync: this, duration: Duration(milliseconds: 350), ); curvedAnimation = CurvedAnimation( curve: Curves.ease, parent: emoticonsAniCtr, ); emoticonsAniCtr.addStatusListener((status) { if (status == AnimationStatus.dismissed) { _checkStart(); } }); super.initState(); } @override Widget build(BuildContext context) { return Consumer( builder: (BuildContext context, RtcProvider provider, Widget? child) { MicRes? micRes = provider.roomWheatMap[widget.index]; if (micRes?.user == null) { return Container(); } String? emojiPath = provider.roomWheatMap[widget.index]?.emojiPath; if (emojiPath != null) { pathList.add(micRes); micRes?.setEmojiPath = null; _checkStart(); } if (playingRes != null) { return FadeTransition( opacity: curvedAnimation, child: playingRes?.type == SCRoomMsgType.roomDice ? FutureBuilder( future: Future.delayed(Duration(milliseconds: 2000)), // 传入Future builder: ( BuildContext context, AsyncSnapshot snapshot, ) { // 根据snapshot的状态来构建UI if (snapshot.connectionState == ConnectionState.done) { return Image.asset( "sc_images/room/sc_icon_dice_${micRes?.number}.png", height: widget.isGameModel ? 38.w : 45.w, ); } else { return Image.asset( "sc_images/room/sc_icon_dice_animl.webp", height: widget.isGameModel ? 38.w : 45.w, ); } }, ) : (playingRes?.type == SCRoomMsgType.roomRPS ? FutureBuilder( future: Future.delayed(Duration(milliseconds: 2000)), // 传入Future builder: ( BuildContext context, AsyncSnapshot snapshot, ) { // 根据snapshot的状态来构建UI if (snapshot.connectionState == ConnectionState.done) { return Image.asset( "sc_images/room/sc_icon_rps_${micRes?.number}.png", height: widget.isGameModel ? 38.w : 45.w, ); } else { return Image.asset( "sc_images/room/sc_icon_rps_animal.webp", height: widget.isGameModel ? 38.w : 45.w, ); } }, ) : netImage( url: playingRes?.number ?? "", width: widget.isGameModel ? 65.w : 75.w, )), ); } else if (giftPath != null) { return FadeTransition( opacity: curvedAnimation, child: netImage( url: "$giftPath", width: widget.isGameModel ? 65.w : 75.w, borderRadius: BorderRadius.circular(4.w), ), ); } return Container(); }, ); } void _checkStart() { print('emoticonsAniCtr.status:${emoticonsAniCtr.status}'); print('emoticonsAniCtr.path:${playingRes}'); if (!showIn) { if (pathList.isNotEmpty) { playingRes = pathList.first; pathList.removeAt(0); print('播放表情:$playingRes'); emoticonsAniCtr.forward(); Future.delayed(Duration(milliseconds: 5)).then((value) { setState(() { showIn = true; }); }); /// 延迟3秒移除表情包 Future.delayed(Duration(seconds: 3)).then((value) { emoticonsAniCtr.reverse(); showIn = false; playingRes = null; _checkStart(); }); } else if (giftList.isNotEmpty) { giftPath = giftList.first; giftList.removeAt(0); print('播放礼物:$playingRes'); emoticonsAniCtr.forward(); Future.delayed(Duration(milliseconds: 10)).then((value) { setState(() { showIn = true; }); }); /// 延迟3秒移除表情包 Future.delayed(Duration(seconds: 1)).then((value) { emoticonsAniCtr.reverse(); showIn = false; giftPath = null; _checkStart(); }); } } } } class Sonic extends StatefulWidget { final num index; final bool isGameModel; final JoinRoomRes? room; const Sonic({ Key? key, required this.index, required this.room, this.isGameModel = false, }) : super(key: key); @override _SonicState createState() => _SonicState(); } class _SonicState extends State with TickerProviderStateMixin { List ctrList = []; int lastIndex = 0; late RtcProvider provider; @override void initState() { // TODO: implement initState super.initState(); for (int i = 0; i < 4; i++) { ctrList.add( AnimationController(duration: Duration(milliseconds: 900), vsync: this), ); } for (var element in ctrList) { element.addStatusListener((status) { if (status == AnimationStatus.completed) { element.reset(); } }); } provider = Provider.of(context, listen: false); provider.addSoundVoiceChangeListener(_onVoiceChange); } @override void dispose() { // TODO: implement dispose provider.removeSoundVoiceChangeListener(_onVoiceChange); for (var element in ctrList) { element.dispose(); } super.dispose(); } @override Widget build(BuildContext context) { return Stack( clipBehavior: Clip.none, children: [ SonicItem(ctrList[0], widget.room, isGameModel: widget.isGameModel), SonicItem(ctrList[1], widget.room, isGameModel: widget.isGameModel), SonicItem(ctrList[2], widget.room, isGameModel: widget.isGameModel), SonicItem(ctrList[3], widget.room, isGameModel: widget.isGameModel), ], ); } void _checkSoundAni(int volume) async { if (volume <= 20) return; // await Future.delayed(Duration(milliseconds: 10)); // var dateTime = DateTime.now(); // for(int i =0 ; i < 5000000;i++){ // lastIndex = 1; // } // print('执行耗时:${DateTime.now().millisecondsSinceEpoch - dateTime.millisecondsSinceEpoch}'); ctrList.forEach((element) { if (element.value == 0) { if (lastIndex == 0) { element.forward(); lastIndex = ctrList.indexOf(element); return; } else { if (ctrList[lastIndex].value >= 0.35 || ctrList[lastIndex].status == AnimationStatus.dismissed) { element.forward(); lastIndex = ctrList.indexOf(element); return; } } } }); } _onVoiceChange(num index, int volum) { print('说话声音:$volum'); if (widget.index == index) { _checkSoundAni(volum); } } } class SonicItem extends AnimatedWidget { Animation animation; JoinRoomRes? room; bool isGameModel = false; SonicItem(this.animation, this.room, {Key? key, this.isGameModel = false}) : super(listenable: animation); @override Widget build(BuildContext context) { return Visibility( visible: animation!.value > 0, child: ScaleTransition( scale: Tween(begin: 1.0, end: 1.4).animate(animation!), child: Container( height: width(isGameModel ? 34 : 52), width: width(isGameModel ? 34 : 52), decoration: (room?.roomProfile?.roomSetting?.roomSpecialMikeType ?? "") .isEmpty || (room?.roomProfile?.roomSetting?.roomSpecialMikeType != SCRoomSpecialMikeType.TYPE_VIP3.name && room?.roomProfile?.roomSetting?.roomSpecialMikeType != SCRoomSpecialMikeType.TYPE_VIP4.name && room?.roomProfile?.roomSetting?.roomSpecialMikeType != SCRoomSpecialMikeType.TYPE_VIP5.name && room?.roomProfile?.roomSetting?.roomSpecialMikeType != SCRoomSpecialMikeType.TYPE_VIP6.name) ? BoxDecoration( shape: BoxShape.circle, color: SocialChatTheme.primaryColor.withOpacity( 1 - animation!.value, ), ) : BoxDecoration(), child: room?.roomProfile?.roomSetting?.roomSpecialMikeType == SCRoomSpecialMikeType.TYPE_VIP3.name ? Image.asset( "sc_images/room/sc_icon_room_vip3_sonic_anim.webp", ) : (room?.roomProfile?.roomSetting?.roomSpecialMikeType == SCRoomSpecialMikeType.TYPE_VIP4.name ? Image.asset( "sc_images/room/sc_icon_room_vip4_sonic_anim.webp", ) : (room?.roomProfile?.roomSetting?.roomSpecialMikeType == SCRoomSpecialMikeType.TYPE_VIP5.name ? Image.asset( "sc_images/room/sc_icon_room_vip5_sonic_anim.webp", ) : (room ?.roomProfile ?.roomSetting ?.roomSpecialMikeType == SCRoomSpecialMikeType.TYPE_VIP6.name ? Image.asset( "sc_images/room/sc_icon_room_vip6_sonic_anim.webp", ) : Container()))), ), ), ); } }