diff --git a/lib/services/audio/rtc_manager.dart b/lib/services/audio/rtc_manager.dart index 3e341d8..78408cc 100644 --- a/lib/services/audio/rtc_manager.dart +++ b/lib/services/audio/rtc_manager.dart @@ -481,6 +481,24 @@ class RealTimeCommunicationManager extends ChangeNotifier { ///禁音开关 默认关闭 bool isMic = true; + bool _setSelfMicMuted(bool value) { + if (isMic == value) { + return false; + } + isMic = value; + return true; + } + + void _notifySelfMicUiState({bool? muted, bool notifyIfUnchanged = false}) { + var changed = false; + if (muted != null) { + changed = _setSelfMicMuted(muted) || changed; + } + if (changed || notifyIfUnchanged) { + notifyListeners(); + } + } + bool shouldShowSeatSoundWave(num index) { final seat = roomWheatMap[index]; final userId = (seat?.user?.id ?? "").trim(); @@ -2530,8 +2548,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { } Future setSelfMicMutedFromBottom(bool muted) async { - isMic = muted; - notifyListeners(); + _notifySelfMicUiState(muted: muted); final currentUserId = (AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim(); final seatIndex = @@ -2539,7 +2556,6 @@ class RealTimeCommunicationManager extends ChangeNotifier { ? userOnMaiInIndex(currentUserId) : (_currentUserMicSeat()?.micIndex ?? -1); if (seatIndex < 0) { - notifyListeners(); return; } final seat = roomWheatMap[seatIndex]; @@ -2557,6 +2573,10 @@ class RealTimeCommunicationManager extends ChangeNotifier { notifyListeners(); } + Future toggleSelfMicMutedFromBottom() { + return setSelfMicMutedFromBottom(!isMic); + } + Future handleSelfMicRemovedByRemote({ bool refreshMicList = true, }) async { @@ -2564,6 +2584,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { final currentUserId = (AccountStorage().getCurrentUser()?.userProfile?.id ?? "").trim(); if (currentUserKeys.isEmpty) { + _notifySelfMicUiState(muted: true, notifyIfUnchanged: true); await _switchRoomRtcToAudience(); return; } @@ -2572,7 +2593,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { currentUserId.isNotEmpty ? userOnMaiInIndex(currentUserId) : (_currentUserMicSeat()?.micIndex ?? -1); - isMic = true; + _setSelfMicMuted(true); _clearSelfMicSwitchGuard(clearPreferredIndex: true); _clearSelfMicGoUpGuard(clearPreferredIndex: true); if (currentSeatIndex > -1) { @@ -2584,7 +2605,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { _roomRtcRoleIsBroadcaster = false; _syncSelfMicRuntimeState(); _clearConfirmedSelfMicIdentity(); - notifyListeners(); + _notifySelfMicUiState(notifyIfUnchanged: true); await _switchRoomRtcToAudience(); if (refreshMicList) { @@ -5079,7 +5100,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { _roomVisualEffectsEnabled = false; _voiceRoomRouteVisible = false; _isExitingCurrentVoiceRoomSession = false; - isMic = true; + _setSelfMicMuted(true); isMusicPlaying = false; if (clearPersistedRoomMarker) { DataPersistence.setLastTimeRoomId(""); @@ -5292,7 +5313,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { user: currentUser, roomToken: micGoUpRes.roomToken, ); - isMic = selfMicMuted; + _setSelfMicMuted(selfMicMuted); _syncSelfMicRuntimeState(); } @@ -5447,6 +5468,8 @@ class RealTimeCommunicationManager extends ChangeNotifier { selfMicMuted: selfMicMuted, ); notifyListeners(); + } else { + _refreshMicListSilently(notifyIfUnchanged: true); } final switched = await _switchRoomRtcToBroadcaster( @@ -5508,7 +5531,7 @@ class RealTimeCommunicationManager extends ChangeNotifier { roomWheatMap[index]?.user, currentUserKeys, )) { - isMic = true; + _setSelfMicMuted(true); } _clearSelfMicSwitchGuard(clearPreferredIndex: true); _clearSelfMicGoUpGuard(clearPreferredIndex: true); @@ -5519,11 +5542,10 @@ class RealTimeCommunicationManager extends ChangeNotifier { } SCHeartbeatUtils.cancelAnchorTimer(); - await _switchRoomRtcToAudience(); _clearCurrentUserFromSeats(); _syncSelfMicRuntimeState(); _clearConfirmedSelfMicIdentity(); - notifyListeners(); + _notifySelfMicUiState(notifyIfUnchanged: true); requestMicrophoneListRefresh( notifyIfUnchanged: false, minInterval: const Duration(milliseconds: 350), diff --git a/lib/ui_kit/widgets/room/floating/floating_lucky_gift_global_screen_widget.dart b/lib/ui_kit/widgets/room/floating/floating_lucky_gift_global_screen_widget.dart index 4cab6a6..6420dcc 100644 --- a/lib/ui_kit/widgets/room/floating/floating_lucky_gift_global_screen_widget.dart +++ b/lib/ui_kit/widgets/room/floating/floating_lucky_gift_global_screen_widget.dart @@ -122,7 +122,7 @@ class _FloatingLuckyGiftGlobalScreenWidgetState resource: FloatingLuckyGiftGlobalScreenWidget.backgroundPagAssetPath, width: FloatingLuckyGiftGlobalScreenWidget.bannerWidth.w, height: FloatingLuckyGiftGlobalScreenWidget.bannerHeight.w, - fit: BoxFit.fill, + fit: BoxFit.contain, clipBehavior: Clip.none, defaultBuilder: (_) => _buildFallbackBackground(), ); diff --git a/lib/ui_kit/widgets/room/room_bottom_widget.dart b/lib/ui_kit/widgets/room/room_bottom_widget.dart index a04f178..67fd0a3 100644 --- a/lib/ui_kit/widgets/room/room_bottom_widget.dart +++ b/lib/ui_kit/widgets/room/room_bottom_widget.dart @@ -38,9 +38,11 @@ class RoomGiftComboFloatingLayer extends StatelessWidget { @override Widget build(BuildContext context) { - return Selector( - selector: (context, provider) => _shouldShowRoomBottomMic(provider), - builder: (context, showMic, child) { + final provider = context.read(); + return AnimatedBuilder( + animation: provider, + builder: (context, child) { + final showMic = _shouldShowRoomBottomMic(provider); return LayoutBuilder( builder: (context, constraints) { final inputWidth = _resolveRoomBottomInputWidth( @@ -81,15 +83,16 @@ class _RoomBottomWidgetState extends State { @override Widget build(BuildContext context) { + final provider = context.read(); return SizedBox( height: _floatingButtonHostHeight.w, - child: Selector( - selector: - (context, provider) => _RoomBottomSnapshot( - showMic: _shouldShowRoomBottomMic(provider), - isMic: provider.isMic, - ), - builder: (context, bottomSnapshot, child) { + child: AnimatedBuilder( + animation: provider, + builder: (context, child) { + final bottomSnapshot = _RoomBottomSnapshot( + showMic: _shouldShowRoomBottomMic(provider), + isMic: provider.isMic, + ); return LayoutBuilder( builder: (context, constraints) { final inputWidth = _resolveRoomBottomInputWidth( @@ -323,11 +326,7 @@ class _RoomBottomWidgetState extends State { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { - final provider = context.read(); - setState(() { - provider.isMic = !provider.isMic; - }); - unawaited(provider.setSelfMicMutedFromBottom(provider.isMic)); + unawaited(context.read().toggleSelfMicMutedFromBottom()); }, child: RoomBottomCircleAction( child: Image.asset( diff --git a/test/rtc_self_mic_snapshot_test.dart b/test/rtc_self_mic_snapshot_test.dart index 92b8563..f62e8ab 100644 --- a/test/rtc_self_mic_snapshot_test.dart +++ b/test/rtc_self_mic_snapshot_test.dart @@ -117,4 +117,27 @@ void main() { expect(rtcProvider.roomWheatMap[5]?.user?.id, 'server-seat-id'); expect(rtcProvider.roomWheatMap[5]?.user?.account, 'server-seat-account'); }); + + test('self mic state follows microphone list snapshots', () { + SharedPreferences.setMockInitialValues({}); + AccountStorage().setCurrentUser( + SocialChatLoginRes( + userProfile: SocialChatUserProfile(id: 'user-1', userNickname: 'Me'), + ), + ); + final rtcProvider = RealTimeCommunicationManager(); + + rtcProvider.debugApplyMicListSnapshot([ + MicRes( + micIndex: 2, + user: SocialChatUserProfile(id: 'user-1', userNickname: 'Me'), + ), + ], notifyIfUnchanged: false); + expect(rtcProvider.isOnMai(), isTrue); + + rtcProvider.debugApplyMicListSnapshot([ + MicRes(micIndex: 2), + ], notifyIfUnchanged: false); + expect(rtcProvider.isOnMai(), isFalse); + }); }