礼物gift以及谷歌service

This commit is contained in:
NIGGER SLAYER 2026-04-15 21:22:05 +08:00
parent 553c37c743
commit 73f1cf1199
8 changed files with 1534 additions and 1410 deletions

View File

@ -1,36 +1,20 @@
{
"project_info": {
"project_number": "980005024266",
"project_id": "yumi-c3b30",
"storage_bucket": "yumi-c3b30.firebasestorage.app"
"project_number": "991697445884",
"project_id": "yumi-chat-party",
"storage_bucket": "yumi-chat-party.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:980005024266:android:581aa38059aa318d9c65f3",
"mobilesdk_app_id": "1:991697445884:android:1d6eff055324e2ba128879",
"android_client_info": {
"package_name": "com.org.yumiparty"
}
},
"oauth_client": [
{
"client_id": "980005024266-o9pjdmdbqqt1julbh1q1ovafcvmr1mv1.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.org.yumiparty",
"certificate_hash": "3fe178dcc3294f7420df2754cfaf4646d6a19478"
}
},
{
"client_id": "980005024266-sl5h466pe90jsjmoi4jcd7bqhmckieec.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.org.yumiparty",
"certificate_hash": "9ab21924bfbe2c56555860ebabf23c4099fd7412"
}
},
{
"client_id": "980005024266-vrrp2us89svbqgc93oe7q4ad7uoh8fl6.apps.googleusercontent.com",
"client_id": "991697445884-6b6losu2td9u0era7qui4aeavm16lj57.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.org.yumiparty",
@ -38,20 +22,28 @@
}
},
{
"client_id": "980005024266-dgtthe3q98k8tk873rfdrsnu5ot61p09.apps.googleusercontent.com",
"client_id": "991697445884-jc3r56gntpgdeicjj8j8qhkght67f7og.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.org.yumiparty",
"certificate_hash": "02a381c217fa1bd986cb00aefb8476efc5bea8e3"
}
},
{
"client_id": "991697445884-3ac3fk104691brjgkcejulijnivgr7o2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAbxU9QpbnC5PrrhUjDSK1camiotDlF3qE"
"current_key": "AIzaSyDLY8LRCaKYapwXt2BvxUoHYD8kdC1fL70"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "980005024266-dgtthe3q98k8tk873rfdrsnu5ot61p09.apps.googleusercontent.com",
"client_id": "991697445884-3ac3fk104691brjgkcejulijnivgr7o2.apps.googleusercontent.com",
"client_type": 3
}
]
@ -60,4 +52,4 @@
}
],
"configuration_version": "1"
}
}

View File

@ -10,7 +10,6 @@ import 'package:provider/provider.dart';
///
///
class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
@override
List<Widget> getHomeTabPages(BuildContext context) {
final List<Widget> pages = [];
@ -25,7 +24,6 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
return tabs;
}
@override
int getHomeInitialTabIndex() {
return 0;
@ -33,7 +31,10 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
@override
void onAvatarTap(BuildContext context) {
Provider.of<SCAppGeneralManager>(context, listen: false).openDrawer(context);
Provider.of<SCAppGeneralManager>(
context,
listen: false,
).openDrawer(context);
}
@override
@ -125,11 +126,6 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
/// === ===
@override
bool shouldShowPasswordRoomIcon() {
//
@ -276,10 +272,7 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
const Color(0xFF3F0810),
];
} else {
return [
const Color(0xFF666666),
const Color(0xFF000000),
];
return [const Color(0xFF666666), const Color(0xFF000000)];
}
}
@ -287,15 +280,9 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
List<Color> getMePageTabIndicatorGradient(bool isFemale) {
// Tab指示器渐变
if (isFemale) {
return [
const Color(0xffC62548),
const Color(0xff9C0322),
];
return [const Color(0xffC62548), const Color(0xff9C0322)];
} else {
return [
const Color(0xff141414),
const Color(0xffE4E4E4),
];
return [const Color(0xff141414), const Color(0xffE4E4E4)];
}
}
@ -446,10 +433,7 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
@override
List<Color> getVipPageButtonGradient() {
// VIP按钮渐变颜色
return [
const Color(0xffFF5722),
const Color(0xffFEB219),
];
return [const Color(0xffFF5722), const Color(0xffFEB219)];
}
@override
@ -519,7 +503,11 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
}
@override
String getVipPagePreviewImage(int vipLevel, String previewType, String featureName) {
String getVipPagePreviewImage(
int vipLevel,
String previewType,
String featureName,
) {
// VIP页面预览图像路径模式
if (featureName == 'profile_frame') {
return "sc_images/vip/sc_icon_vip${vipLevel}_profile_rev.png";
@ -666,10 +654,7 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
@override
List<Color> getAdminEditingButtonGradient(String buttonType) {
// 使
return [
const Color(0xffC670FF),
const Color(0xff7726FF),
];
return [const Color(0xffC670FF), const Color(0xff7726FF)];
}
@override
@ -699,10 +684,7 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
return BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: const Color(0xffE6E6E6),
width: 1.0,
),
border: Border.all(color: const Color(0xffE6E6E6), width: 1.0),
);
}
@ -722,10 +704,7 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
Map<String, int> getAdminEditingViolationTypeMapping(String targetType) {
// ID映射
if (targetType == 'User') {
return {
'Pornography': 1,
'Illegal information': 2,
};
return {'Pornography': 1, 'Illegal information': 2};
} else if (targetType == 'Room') {
return {
'Pornography': 3,
@ -748,10 +727,7 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
@override
List<Color> getAdminSearchButtonGradient(String pageType) {
// -
return [
Colors.transparent,
Colors.transparent,
];
return [Colors.transparent, Colors.transparent];
}
@override
@ -888,10 +864,7 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
@override
List<Color> getEditProfileGenderButtonGradient(bool isMale, bool isSelected) {
//
return [
const Color(0xffFF9326),
const Color(0xffFEB219),
];
return [const Color(0xffFF9326), const Color(0xffFEB219)];
}
@override
@ -1203,11 +1176,14 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
case 'ACTIVITY':
return "sc_images/room/sc_icon_activity_gift_head_bg.png";
case 'LUCK':
return "sc_images/room/sc_icon_luck_gift_head_bg.png";
// Fallback to the activity header until dedicated localized assets land.
return "sc_images/room/sc_icon_activity_gift_head_bg.png";
case 'CP':
return "sc_images/room/sc_icon_cp_gift_head_bg.png";
// Fallback to the activity header until dedicated localized assets land.
return "sc_images/room/sc_icon_activity_gift_head_bg.png";
case 'MAGIC':
return "sc_images/room/sc_icon_magic_gift_head_bg.png";
// Fallback to the activity header until dedicated localized assets land.
return "sc_images/room/sc_icon_activity_gift_head_bg.png";
default:
return "sc_images/room/sc_icon_activity_gift_head_bg.png";
}
@ -1520,10 +1496,7 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
// Tab标签选中文本样式
// 15"MyCustomFont"
// ScreenUtil().sp
return const TextStyle(
fontSize: 15,
fontFamily: "MyCustomFont",
);
return const TextStyle(fontSize: 15, fontFamily: "MyCustomFont");
}
@override
@ -1531,10 +1504,7 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
// Tab标签未选中文本样式
// 13"MyCustomFont"
// ScreenUtil().sp
return const TextStyle(
fontSize: 13,
fontFamily: "MyCustomFont",
);
return const TextStyle(fontSize: 13, fontFamily: "MyCustomFont");
}
@override
@ -1955,20 +1925,14 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
TextStyle getDynamicPageTabLabelStyle() {
// Tab标签选中文本样式 - TextStyle(fontWeight: FontWeight.bold, fontSize: 16.sp)
// 使.sp单位使ScreenUtil()
return const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
);
return const TextStyle(fontWeight: FontWeight.bold, fontSize: 16);
}
@override
TextStyle getDynamicPageTabUnselectedLabelStyle() {
// Tab标签未选中文本样式 - TextStyle(fontWeight: FontWeight.normal, fontSize: 15.sp)
// 使.sp单位使ScreenUtil()
return const TextStyle(
fontWeight: FontWeight.normal,
fontSize: 15,
);
return const TextStyle(fontWeight: FontWeight.normal, fontSize: 15);
}
/// === ===
@ -2568,9 +2532,10 @@ class BaseBusinessLogicStrategy implements BusinessLogicStrategy {
// Scaffold背景颜色 - Colors.transparent
return Colors.transparent;
}
@override
Color getGoldRecordPageListBackgroundColor() {
// -
return const Color(0xff1a1a1a); // #1a1a1a
}
}
}

View File

@ -11,7 +11,6 @@ import '../../../modules/home/popular/party/sc_home_party_page.dart';
///
///
class Variant1BusinessLogicStrategy extends BaseBusinessLogicStrategy {
/// Tab顺序
@override
List<Widget> getHomeTabPages(BuildContext context) {
@ -134,11 +133,6 @@ class Variant1BusinessLogicStrategy extends BaseBusinessLogicStrategy {
/// === ===
@override
bool shouldShowPasswordRoomIcon() {
// UI
@ -1183,11 +1177,14 @@ class Variant1BusinessLogicStrategy extends BaseBusinessLogicStrategy {
case 'ACTIVITY':
return "sc_images/room/sc_icon_activity_gift_head_bg.png";
case 'LUCK':
return "sc_images/room/sc_icon_luck_gift_head_bg.png";
// Fallback to the activity header until dedicated localized assets land.
return "sc_images/room/sc_icon_activity_gift_head_bg.png";
case 'CP':
return "sc_images/room/sc_icon_cp_gift_head_bg.png";
// Fallback to the activity header until dedicated localized assets land.
return "sc_images/room/sc_icon_activity_gift_head_bg.png";
case 'MAGIC':
return "sc_images/room/sc_icon_magic_gift_head_bg.png";
// Fallback to the activity header until dedicated localized assets land.
return "sc_images/room/sc_icon_activity_gift_head_bg.png";
default:
return "sc_images/room/sc_icon_activity_gift_head_bg.png";
}
@ -1531,7 +1528,7 @@ class Variant1BusinessLogicStrategy extends BaseBusinessLogicStrategy {
@override
Color getRechargePageScaffoldBackgroundColor() {
// Scaffold背景颜色 -
return Colors.transparent; // #1a1a1a
return Colors.transparent; // #1a1a1a
}
@override
@ -1668,7 +1665,7 @@ class Variant1BusinessLogicStrategy extends BaseBusinessLogicStrategy {
@override
Color getGoldRecordPageScaffoldBackgroundColor() {
// Scaffold背景颜色 -
return Colors.transparent; // #1a1a1a
return Colors.transparent; // #1a1a1a
}
@override
@ -1811,13 +1808,13 @@ class Variant1BusinessLogicStrategy extends BaseBusinessLogicStrategy {
@override
Color getStoreItemUnselectedBorderColor() {
// Store商品项未选中边框颜色 -
return Colors.transparent; // Colors.black12
return Colors.transparent; // Colors.black12
}
@override
Color getStoreItemSelectedBorderColor() {
// Store商品项选中边框颜色 - 使
return SocialChatTheme.primaryLight; // Color(0xffFF9500)
return SocialChatTheme.primaryLight; // Color(0xffFF9500)
}
@override
@ -1829,7 +1826,7 @@ class Variant1BusinessLogicStrategy extends BaseBusinessLogicStrategy {
@override
Color getStoreItemPriceTextColor() {
// Store商品项价格文本颜色 - 使
return SocialChatTheme.primaryLight; // Colors.black
return SocialChatTheme.primaryLight; // Colors.black
}
@override
@ -2061,7 +2058,7 @@ class Variant1BusinessLogicStrategy extends BaseBusinessLogicStrategy {
@override
Color getSearchPageScaffoldBackgroundColor() {
// Scaffold背景颜色 - 使
return Colors.transparent; // Colors.white
return Colors.transparent; // Colors.white
}
@override
@ -2436,7 +2433,7 @@ class Variant1BusinessLogicStrategy extends BaseBusinessLogicStrategy {
@override
Color getSettingsPageContainerBorderColor() {
// - 使
return Colors.transparent;
return Colors.transparent;
}
@override
@ -2560,6 +2557,6 @@ class Variant1BusinessLogicStrategy extends BaseBusinessLogicStrategy {
@override
Color getGoldRecordPageListBackgroundColor() {
// -
return Colors.transparent; // #1a1a1a
return Colors.transparent; // #1a1a1a
}
}

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@ const Duration _kGiftPageSkeletonMaxDuration = Duration(milliseconds: 900);
class GiftTabPage extends StatefulWidget {
final String type;
final bool isDark;
final Function(int checkedIndex) checkedCall;
final ValueChanged<SocialChatGiftRes?> checkedCall;
const GiftTabPage(
this.type,
@ -53,13 +53,7 @@ class _GiftTabPageState extends State<GiftTabPage>
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.type == "CUSTOMIZED") {
widget.checkedCall(1);
} else {
widget.checkedCall(0);
}
});
checkedIndex = widget.type == "CUSTOMIZED" ? 1 : 0;
}
@override
@ -522,7 +516,7 @@ class _GiftTabPageState extends State<GiftTabPage>
onTap: () {
setState(() {
checkedIndex = ref.giftByTab[widget.type]!.indexOf(gift);
widget.checkedCall(checkedIndex);
widget.checkedCall(gift);
});
},
);

View File

@ -109,13 +109,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
onError: (ErrorCodeType err, String msg) {
print('rtc错误${err}');
},
onLocalAudioStateChanged:
(
RtcConnection connection,
LocalAudioStreamState state,
LocalAudioStreamReason reason,
) {},
onAudioRoutingChanged: (routing) {},
onAudioMixingStateChanged: (
AudioMixingStateType state,
AudioMixingReasonType reason,
@ -132,15 +125,6 @@ class RealTimeCommunicationManager extends ChangeNotifier {
break;
}
},
onRemoteAudioStateChanged: (
RtcConnection connection,
int remoteUid,
RemoteAudioState state,
RemoteAudioStateReason reason,
int elapsed,
) {
// print('用户 $remoteUid 音频状态: $state, 原因: $reason');
},
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
print('rtc 自己加入 ${connection.channelId} ${connection.localUid}');
},
@ -1037,6 +1021,10 @@ class RealTimeCommunicationManager extends ChangeNotifier {
context!,
listen: false,
).engine?.setClientRole(role: ClientRoleType.clientRoleBroadcaster);
Provider.of<RtcProvider>(
context!,
listen: false,
).engine?.muteLocalAudioStream(false);
}
}
}

View File

@ -1,233 +1,214 @@
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/ui_kit/widgets/room/room_menu_dialog.dart';
import 'package:yumi/ui_kit/widgets/room/room_msg_input.dart';
import 'package:provider/provider.dart';
import 'package:yumi/app_localizations.dart';
import 'package:yumi/ui_kit/components/text/sc_text.dart';
import 'package:yumi/shared/tools/sc_room_utils.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
import 'package:yumi/services/audio/rtc_manager.dart';
import 'package:yumi/modules/gift/gift_page.dart';
import '../../../app/routes/sc_fluro_navigator.dart';
import '../../../modules/index/main_route.dart';
import '../../../services/audio/rtm_manager.dart';
import '../../components/sc_debounce_widget.dart';
class RoomBottomWidget extends StatefulWidget {
@override
_RoomBottomWidgetState createState() => _RoomBottomWidgetState();
}
class _RoomBottomWidgetState extends State<RoomBottomWidget> {
int roomMenuStime1 = 0;
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
Container(
height: 55.w,
margin: EdgeInsetsDirectional.only(start: 25.w),
child: Row(
children: [
SCDebounceWidget(
onTap: () {
if (SCRoomUtils.touristCanMsg(context)) {
Navigator.push(
context,
PopRoute(child: RoomMsgInput()),
);
}
}, child: Image.asset(
"sc_images/room/icon_room_input_t.png",
width: 30.w,
height: 30.w,
),
),
Spacer(),
Consumer<RtcProvider>(
builder: (context, ref, child) {
return _mic1(ref);
},
),
SizedBox(width: 10.w),
SCDebounceWidget(
child: Image.asset(
"sc_images/room/sc_icon_botton_menu.png",
width: 30.w,
height: 30.w,
fit: BoxFit.contain,
),
onTap: () {
SmartDialog.show(
tag: "showRoomMenuDialog",
alignment: Alignment.bottomCenter,
debounce: true,
animationType: SmartAnimationType.fade,
maskColor: Colors.transparent,
clickMaskDismiss: true,
builder: (_) {
return RoomMenuDialog(roomMenuStime1, (eTime) {
roomMenuStime1 = eTime;
});
},
);
},
),
SizedBox(width: 10.w),
SCDebounceWidget(
child: Selector<RtmProvider, int>(
selector: (c, p) => p.allUnReadCount,
shouldRebuild: (prev, next) => prev != next,
builder: (_, allUnReadCount, __) {
return allUnReadCount > 0
? Badge(
backgroundColor: Colors.red,
label: text(
"${allUnReadCount > 99 ? "99+" : allUnReadCount}",
fontSize: 9.sp,
textColor: Colors.white,
fontWeight: FontWeight.w600,
),
alignment: AlignmentDirectional.topEnd,
child: Image.asset(
"sc_images/room/sc_icon_botton_message.png",
width: 30.w,
height: 30.w,
fit: BoxFit.contain,
),
)
: Image.asset(
"sc_images/room/sc_icon_botton_message.png",
width: 30.w,
height: 30.w,
fit: BoxFit.contain,
);
},
),
onTap: () {
SCNavigatorUtils.push(
context,
"${SCMainRoute.message}?isFromRoom=true",
);
},
),
SizedBox(width: 15.w),
],
),
),
PositionedDirectional(
bottom: 8.w,
child: SCDebounceWidget(
onTap: () {
SmartDialog.show(
tag: "showGiftControl",
alignment: Alignment.bottomCenter,
maskColor: Colors.transparent,
animationType: SmartAnimationType.fade,
clickMaskDismiss: true,
builder: (_) {
return GiftPage();
},
);
},
child: Image.asset(
"sc_images/room/sc_icon_botton_gift.png",
width: 45.w,
height: 45.w,
fit: BoxFit.contain,
),
),
),
],
);
}
_mic1(RtcProvider provider) {
///
bool show = false;
///
provider.roomWheatMap.forEach((k, v) {
if (v.user?.id == AccountStorage().getCurrentUser()?.userProfile?.id) {
show = true;
}
});
return Visibility(
visible: show,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
provider.isMic = !provider.isMic;
///
provider.roomWheatMap.forEach((k, v) {
if (v.user?.id ==
AccountStorage().getCurrentUser()?.userProfile?.id &&
!provider.roomWheatMap[k]!.micMute!) {
if (!provider.isMic) {
Provider.of<RtcProvider>(
context,
listen: false,
).engine?.adjustRecordingSignalVolume(100);
Provider.of<RtcProvider>(
context,
listen: false,
).engine?.setClientRole(
role: ClientRoleType.clientRoleBroadcaster,
);
} else {
if (Provider.of<RtcProvider>(
context,
listen: false,
).isMusicPlaying) {
Provider.of<RtcProvider>(
context,
listen: false,
).engine?.adjustRecordingSignalVolume(0);
} else {
Provider.of<RtcProvider>(
context,
listen: false,
).engine?.setClientRole(
role: ClientRoleType.clientRoleAudience,
);
Provider.of<RtcProvider>(
context,
listen: false,
).engine?.muteLocalAudioStream(provider.isMic);
}
}
}
});
});
},
child: Padding(
padding: EdgeInsets.only(right: 12.w),
child: Container(
width: 30.w,
height: 30.w,
alignment: Alignment.center,
// decoration: BoxDecoration(
// color: Colors.white.withOpacity(0.2), shape: BoxShape.circle
// ),
child: Image.asset(
"sc_images/room/${provider.isMic ? 'sc_icon_botton_mic_close' : 'sc_icon_botton_mic_open'}.png",
width: 30.w,
fit: BoxFit.fill,
gaplessPlayback: true,
),
),
),
),
);
}
}
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/ui_kit/widgets/room/room_menu_dialog.dart';
import 'package:yumi/ui_kit/widgets/room/room_msg_input.dart';
import 'package:provider/provider.dart';
import 'package:yumi/ui_kit/components/text/sc_text.dart';
import 'package:yumi/shared/tools/sc_room_utils.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
import 'package:yumi/services/audio/rtc_manager.dart';
import 'package:yumi/modules/gift/gift_page.dart';
import '../../../app/routes/sc_fluro_navigator.dart';
import '../../../modules/index/main_route.dart';
import '../../../services/audio/rtm_manager.dart';
import '../../components/sc_debounce_widget.dart';
class RoomBottomWidget extends StatefulWidget {
const RoomBottomWidget({super.key});
@override
_RoomBottomWidgetState createState() => _RoomBottomWidgetState();
}
class _RoomBottomWidgetState extends State<RoomBottomWidget> {
int roomMenuStime1 = 0;
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
Container(
height: 55.w,
margin: EdgeInsetsDirectional.only(start: 25.w),
child: Row(
children: [
SCDebounceWidget(
onTap: () {
if (SCRoomUtils.touristCanMsg(context)) {
Navigator.push(context, PopRoute(child: RoomMsgInput()));
}
},
child: Image.asset(
"sc_images/room/icon_room_input_t.png",
width: 30.w,
height: 30.w,
),
),
Spacer(),
Consumer<RtcProvider>(
builder: (context, ref, child) {
return _mic1(ref);
},
),
SizedBox(width: 10.w),
SCDebounceWidget(
child: Image.asset(
"sc_images/room/sc_icon_botton_menu.png",
width: 30.w,
height: 30.w,
fit: BoxFit.contain,
),
onTap: () {
SmartDialog.show(
tag: "showRoomMenuDialog",
alignment: Alignment.bottomCenter,
debounce: true,
animationType: SmartAnimationType.fade,
maskColor: Colors.transparent,
clickMaskDismiss: true,
builder: (_) {
return RoomMenuDialog(roomMenuStime1, (eTime) {
roomMenuStime1 = eTime;
});
},
);
},
),
SizedBox(width: 10.w),
SCDebounceWidget(
child: Selector<RtmProvider, int>(
selector: (c, p) => p.allUnReadCount,
shouldRebuild: (prev, next) => prev != next,
builder: (_, allUnReadCount, __) {
return allUnReadCount > 0
? Badge(
backgroundColor: Colors.red,
label: text(
"${allUnReadCount > 99 ? "99+" : allUnReadCount}",
fontSize: 9.sp,
textColor: Colors.white,
fontWeight: FontWeight.w600,
),
alignment: AlignmentDirectional.topEnd,
child: Image.asset(
"sc_images/room/sc_icon_botton_message.png",
width: 30.w,
height: 30.w,
fit: BoxFit.contain,
),
)
: Image.asset(
"sc_images/room/sc_icon_botton_message.png",
width: 30.w,
height: 30.w,
fit: BoxFit.contain,
);
},
),
onTap: () {
SCNavigatorUtils.push(
context,
"${SCMainRoute.message}?isFromRoom=true",
);
},
),
SizedBox(width: 15.w),
],
),
),
PositionedDirectional(
bottom: 8.w,
child: SCDebounceWidget(
onTap: () {
SmartDialog.show(
tag: "showGiftControl",
alignment: Alignment.bottomCenter,
maskColor: Colors.transparent,
animationType: SmartAnimationType.fade,
clickMaskDismiss: true,
builder: (_) {
return GiftPage();
},
);
},
child: Image.asset(
"sc_images/room/sc_icon_botton_gift.png",
width: 45.w,
height: 45.w,
fit: BoxFit.contain,
),
),
),
],
);
}
_mic1(RtcProvider provider) {
///
bool show = false;
///
provider.roomWheatMap.forEach((k, v) {
if (v.user?.id == AccountStorage().getCurrentUser()?.userProfile?.id) {
show = true;
}
});
return Visibility(
visible: show,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
provider.isMic = !provider.isMic;
///
provider.roomWheatMap.forEach((k, v) {
if (v.user?.id ==
AccountStorage().getCurrentUser()?.userProfile?.id &&
!provider.roomWheatMap[k]!.micMute!) {
if (!provider.isMic) {
provider.engine?.adjustRecordingSignalVolume(100);
provider.engine?.setClientRole(
role: ClientRoleType.clientRoleBroadcaster,
);
provider.engine?.muteLocalAudioStream(false);
} else {
if (provider.isMusicPlaying) {
provider.engine?.adjustRecordingSignalVolume(0);
} else {
provider.engine?.setClientRole(
role: ClientRoleType.clientRoleAudience,
);
provider.engine?.muteLocalAudioStream(provider.isMic);
}
}
}
});
});
},
child: Padding(
padding: EdgeInsets.only(right: 12.w),
child: Container(
width: 30.w,
height: 30.w,
alignment: Alignment.center,
// decoration: BoxDecoration(
// color: Colors.white.withOpacity(0.2), shape: BoxShape.circle
// ),
child: Image.asset(
"sc_images/room/${provider.isMic ? 'sc_icon_botton_mic_close' : 'sc_icon_botton_mic_open'}.png",
width: 30.w,
fit: BoxFit.fill,
gaplessPlayback: true,
),
),
),
),
);
}
}

View File

@ -13,6 +13,11 @@
- 本轮按需求暂未处理网络链路上的启动等待,例如审核态检查或远端启动页配置请求。
## 已完成模块
- 已排查语言房“双设备、不同账号进入同一房间,上麦后无声”的直接原因:当前 RTC 进房时统一以 Agora `Audience` 身份加入频道,见 `lib/services/audio/rtc_manager.dart``joinChannel()``clientRoleType: clientRoleAudience`;而本地麦克风开关 `isMic` 默认值为 `true`(当前语义实际是“闭麦”),上麦 `shangMai()` 后只有在 `!isMic` 时才会切到 `Broadcaster` 并取消本地静音,所以“只上麦、不点底部麦克风按钮”时不会发声。
- 已补充语言房当前真实交互结论:现有实现里“上麦”和“开麦”是两个动作。用户点空麦位后只是占麦;还需要再点击底部麦克风按钮,触发 `lib/ui_kit/widgets/room/room_bottom_widget.dart` 中的角色切换,才能真正开始向房间发送音频。
- 已同步补充该需求的后续决策点:如果产品预期是“上麦即能说话”,则需要单独改需求并调整实现为“上麦成功后自动切 `Broadcaster` 且自动开麦”;如果继续保留当前双步骤交互,则至少要补一条明确提示文案/引导,并把“双设备不同账号同房,上麦后需手动开麦才能互听”加入验收用例,避免测试误判为 RTC 故障。
- 已重新收敛语言房无声修复方案:撤回上一轮会影响进房的较大改动后,当前改为只做最小行为修补,不触碰进房和 token/join 链路;已在“底部麦克风点开”和“麦位解除静音且本人处于开麦态”两条路径上补齐 `muteLocalAudioStream(false)`,修正 UI 显示已开麦但 Agora 本地音频流仍保持静音的问题。
- 已按测试收尾要求移除语言房 RTC 诊断面板:该组件仅用于当前无声问题的真机排查,现已从房间页和相关临时诊断代码中整体删除,不作为正式功能保留。
- 已为语言房页面补充右下方 `game` 悬浮入口:入口位于聊天区右下侧、距右侧约 `15.w`、位于底部操作栏上方约 `100.w`,当前先使用代码绘制的 `🎮` 占位图标,并已在实现处标注后续替换为正式 UI 图片资源。
- 已新增语言房游戏底部弹窗:点击 `game` 入口后会从底部弹出约三分之一屏高的面板,采用房间页现有半透明磨砂风格,便于后续继续沿用当前视觉体系。
- 已补齐语言房游戏列表占位结构:弹窗内部先用静态游戏数据驱动,并按“`ListView` 可上下滑动 + 每行 `5` 个图标位”的方式实现,后续只需替换入口图、列表图标和真实接口数据即可继续开发。