yumi-flutter/lib/ui_kit/widgets/room/seat/room_seat_widget.dart
2026-05-22 17:35:49 +08:00

303 lines
8.9 KiB
Dart

import 'package:flutter/cupertino.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:provider/provider.dart';
import 'package:yumi/shared/tools/sc_cp_relation_pair_utils.dart';
import 'package:yumi/services/audio/rtc_manager.dart';
import 'package:yumi/ui_kit/widgets/room/seat/room_mic_relation_effect.dart';
import '../../../../modules/room/seat/sc_seat_item.dart';
class RoomSeatWidget extends StatefulWidget {
const RoomSeatWidget({super.key});
@override
State<RoomSeatWidget> createState() => _RoomSeatWidgetState();
}
class _RoomSeatWidgetState extends State<RoomSeatWidget> {
static const int _defaultVoiceRoomSeatCount = 10;
static const int _seatColumnCount = 5;
static const double _relationEffectWidth = 108;
static const double _relationEffectHeight = 54;
static const double _relationEffectTop = -2;
int _lastSeatCount = 0;
int _normalizeSeatCount(int? seatCount) {
switch (seatCount) {
case 5:
case 10:
case 15:
case 20:
return seatCount ?? 0;
default:
return 0;
}
}
int _resolvedSeatCount(_RoomSeatLayoutSnapshot snapshot) {
final int liveSeatCount = _normalizeSeatCount(snapshot.seatCount);
final int configuredSeatCount = _normalizeSeatCount(
snapshot.configuredSeatCount,
);
final int seatCount =
configuredSeatCount > 0 ? configuredSeatCount : liveSeatCount;
if (!snapshot.isExitingCurrentVoiceRoomSession && seatCount > 0) {
_lastSeatCount = seatCount;
}
if (snapshot.isExitingCurrentVoiceRoomSession && _lastSeatCount > 0) {
return _lastSeatCount;
}
if (snapshot.hasCurrentRoom && seatCount == 0) {
return _lastSeatCount > 0 ? _lastSeatCount : _defaultVoiceRoomSeatCount;
}
return seatCount;
}
int _seatCountFromMicIndexes(Iterable<num> micIndexes) {
var maxIndex = -1;
var count = 0;
for (final micIndex in micIndexes) {
count += 1;
final resolvedIndex = micIndex.toInt();
if (resolvedIndex > maxIndex) {
maxIndex = resolvedIndex;
}
}
final indexedSeatCount = _normalizeSeatCount(maxIndex + 1);
if (indexedSeatCount > 0) {
return indexedSeatCount;
}
return _normalizeSeatCount(count);
}
int _seatContentVersion(RtcProvider provider) {
var version = provider.roomWheatMap.length;
final micIndexes =
provider.roomWheatMap.keys.toList()..sort((a, b) => a.compareTo(b));
for (final micIndex in micIndexes) {
final seat = provider.micAtIndexForDisplay(micIndex);
final user = seat?.user;
version = Object.hash(
version,
micIndex,
seat?.micLock ?? false,
seat?.micMute ?? false,
user?.id ?? "",
user?.userAvatar ?? "",
user?.userNickname ?? "",
user?.roles ?? "",
user?.getHeaddress()?.sourceUrl ?? "",
user?.getHeaddress()?.cover ?? "",
user?.getVIP()?.name ?? "",
scCpProfileRelationVersion(user),
);
}
return version;
}
@override
Widget build(BuildContext context) {
return Selector<RtcProvider, _RoomSeatLayoutSnapshot>(
selector:
(context, provider) => _RoomSeatLayoutSnapshot(
seatCount: _seatCountFromMicIndexes(provider.roomWheatMap.keys),
seatContentVersion: _seatContentVersion(provider),
showRoomVisualEffects: provider.shouldShowRoomVisualEffects,
configuredSeatCount:
provider.currenRoom?.roomProfile?.roomSetting?.mikeSize
?.toInt() ??
provider.previewRoomSeatCount ??
0,
hasCurrentRoom: provider.currenRoom != null,
isExitingCurrentVoiceRoomSession:
provider.isExitingCurrentVoiceRoomSession,
),
builder: (context, snapshot, child) {
final int seatCount = _resolvedSeatCount(snapshot);
return seatCount == 5
? _buildSeat5()
: (seatCount == 10
? _buildSeat10()
: (seatCount == 15
? _buildSeat15()
: (seatCount == 20
? _buildSeat20()
: Container(height: 180.w))));
},
);
}
_buildSeat5() {
return Container(
margin: EdgeInsets.only(top: 3.w, left: 10.w, right: 10.w),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildSeatRow(const [0, 1, 2, 3, 4]),
],
),
);
}
_buildSeat10() {
return Container(
margin: EdgeInsets.only(top: 3.w, left: 10.w, right: 10.w),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildSeatRow(const [0, 1, 2, 3, 4]),
_buildSeatRow(const [5, 6, 7, 8, 9]),
],
),
);
}
_buildSeat15() {
return Container(
margin: EdgeInsets.only(top: 3.w, left: 10.w, right: 10.w),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildSeatRow(const [0, 1, 2, 3, 4]),
_buildSeatRow(const [5, 6, 7, 8, 9]),
_buildSeatRow(const [10, 11, 12, 13, 14]),
],
),
);
}
_buildSeat20() {
return Container(
margin: EdgeInsets.only(top: 3.w, left: 10.w, right: 10.w),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildSeatRow(const [0, 1, 2, 3, 4]),
_buildSeatRow(const [5, 6, 7, 8, 9]),
_buildSeatRow(const [10, 11, 12, 13, 14]),
_buildSeatRow(const [15, 16, 17, 18, 19]),
],
),
);
}
Widget _buildSeatRow(List<int> indexes) {
return LayoutBuilder(
builder: (context, constraints) {
final rowWidth = constraints.maxWidth;
final columnWidth = rowWidth / _seatColumnCount;
return Stack(
clipBehavior: Clip.none,
children: [
Row(
children: [
for (final index in indexes)
Expanded(flex: 1, child: SCSeatItem(index: index)),
],
),
if (rowWidth.isFinite)
..._buildRelationEffectsForRow(
context,
indexes: indexes,
columnWidth: columnWidth,
),
],
);
},
);
}
List<Widget> _buildRelationEffectsForRow(
BuildContext context, {
required List<int> indexes,
required double columnWidth,
}) {
final provider = context.read<RtcProvider>();
if (!provider.shouldShowRoomVisualEffects) {
return const <Widget>[];
}
final effects = <Widget>[];
for (var slot = 0; slot < indexes.length - 1; slot += 1) {
final relationInfo = _relationInfoBetweenSeats(
provider,
indexes[slot],
indexes[slot + 1],
);
if (relationInfo == null) {
continue;
}
final left = ((slot + 1) * columnWidth - (_relationEffectWidth.w / 2))
.clamp(0.0, double.infinity);
effects.add(
Positioned(
left: left,
top: _relationEffectTop.w,
width: _relationEffectWidth.w,
height: _relationEffectHeight.w,
child: IgnorePointer(
child: RoomMicRelationEffect(
relationType: relationInfo.relationType,
cpLevel: relationInfo.cpLevel,
),
),
),
);
}
return effects;
}
SCCpRelationPairInfo? _relationInfoBetweenSeats(
RtcProvider provider,
int leftIndex,
int rightIndex,
) {
final leftUser = provider.micAtIndexForDisplay(leftIndex)?.user;
final rightUser = provider.micAtIndexForDisplay(rightIndex)?.user;
return scCpRelationInfoBetweenProfiles(leftUser, rightUser);
}
}
class _RoomSeatLayoutSnapshot {
const _RoomSeatLayoutSnapshot({
required this.seatCount,
required this.seatContentVersion,
required this.showRoomVisualEffects,
required this.configuredSeatCount,
required this.hasCurrentRoom,
required this.isExitingCurrentVoiceRoomSession,
});
final int seatCount;
final int seatContentVersion;
final bool showRoomVisualEffects;
final int configuredSeatCount;
final bool hasCurrentRoom;
final bool isExitingCurrentVoiceRoomSession;
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
return other is _RoomSeatLayoutSnapshot &&
other.seatCount == seatCount &&
other.seatContentVersion == seatContentVersion &&
other.showRoomVisualEffects == showRoomVisualEffects &&
other.configuredSeatCount == configuredSeatCount &&
other.hasCurrentRoom == hasCurrentRoom &&
other.isExitingCurrentVoiceRoomSession ==
isExitingCurrentVoiceRoomSession;
}
@override
int get hashCode => Object.hash(
seatCount,
seatContentVersion,
showRoomVisualEffects,
configuredSeatCount,
hasCurrentRoom,
isExitingCurrentVoiceRoomSession,
);
}