chatapp3-flutter/lib/modules/room/seat/sc_seat_item.dart
2026-04-09 21:32:23 +08:00

539 lines
19 KiB
Dart

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<SCSeatItem> with TickerProviderStateMixin {
RtcProvider? provider;
JoinRoomRes? room;
MicRes? roomSeat;
final GlobalKey _targetKey = GlobalKey();
@override
void initState() {
super.initState();
provider = Provider.of<RtcProvider>(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<RtcProvider>(
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<Emoticons> with TickerProviderStateMixin {
late AnimationController emoticonsAniCtr;
late CurvedAnimation curvedAnimation;
bool showIn = false;
MicRes? playingRes;
List<MicRes?> pathList = [];
String? giftPath;
List<String> 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<RtcProvider>(
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<void>(
future: Future.delayed(Duration(milliseconds: 2000)),
// 传入Future
builder: (
BuildContext context,
AsyncSnapshot<void> 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<void>(
future: Future.delayed(Duration(milliseconds: 2000)),
// 传入Future
builder: (
BuildContext context,
AsyncSnapshot<void> 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<Sonic> with TickerProviderStateMixin {
List<AnimationController> 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<RtcProvider>(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<double> 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()))),
),
),
);
}
}