Seat Management
Description of the Feature
Seat Management is a real-time interaction and communication method where anchors can interact with the audience on the seat in real-time. Whether it's answering questions, sharing experiences, or entertainment interactions, it greatly enhances the audience's sense of participation and satisfaction. This direct interaction and communication provide more convenient and efficient channels for commercial operations. Using this feature, you can achieve applying to join the seat, inviting to take seat, moving seat positions, kicking someone off the seat, and locking seats, greatly enriching the playability of the voice chat room.
Seat List | Audience applies to take seat | Anchor Handling Seat Request |
![]() | ![]() | ![]() |
Apply To take Seat Process
Audience applies to take seat process
The TUILiveKit audience application for seat feature is mainly implemented through SeatGridView. You can call the following API Function to achieve the audience application for seat feature. The implementation is as follows, with Audience B applying for a seat as an example.
Audience sends a request to join the seat
Audience B sends a request to join the seat to Anchor A. Anchor A will receive the join request from Audience B in the onSeatRequestReceived Callback.
val seatIndex = 1;val timeout = 60;seatGridView.takeSeat(seatIndex, timeout, object : VoiceRoomDefine.RequestCallback {override fun onAccepted(userInfo: TUIRoomDefine.UserInfo) {Log.i(TAG, "Application for speaking is approved")}override fun onRejected(userInfo: TUIRoomDefine.UserInfo) {Log.i(TAG, "Application for speaking is rejected")}override fun onCancelled(userInfo: TUIRoomDefine.UserInfo) {Log.i(TAG, "Application for speaking is canceled")}override fun onTimeout(userInfo: TUIRoomDefine.UserInfo) {Log.i(TAG, "Application for speaking times out")}override fun onError(userInfo: TUIRoomDefine.UserInfo, error: TUICommonDefine.Error, message: String) {Log.i(TAG, "Application for speaking error")}})
int seatIndex = 1;int timeout = 60;seatGridView.takeSeat(seatIndex, timeout, new VoiceRoomDefine.RequestCallback() {@Overridepublic void onAccepted(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "Application for speaking is approved");}@Overridepublic void onRejected(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "Application for speaking is rejected");}@Overridepublic void onCancelled(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "Application for speaking is canceled");}@Overridepublic void onTimeout(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "Application for speaking times out");}@Overridepublic void onError(TUIRoomDefine.UserInfo userInfo, TUICommonDefine.Error error, String message) {Log.i(TAG, "Application for speaking error");}});
import RTCRoomEngineimport LiveStreamCorelet seatIndex = 1let timeout = 60seatGridView.takeSeat(index: index, timeout: timeout) { userInfo inprint("Application for speaking is approved")} onRejected: { userInfo inprint("Application for speaking is rejected")} onCancelled: { userInfo inprint("Application for speaking is canceled")} onTimeout: { userInfo inprint("Application for speaking times out")} onError: { userInfo, code, message inprint("Application for speaking error")}
import 'package:live_stream_core/live_stream_core.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';const int seatIndex = 1;const int timeout = 60;final controller = SeatGridController();final result = await controller.takeSeat(seatIndex, timeout);if (result.code == TUIError.success) {switch (result.type) {case RequestResultType.onAccepted:debugPrint('Request to speak approved');break;case RequestResultType.onRejected:debugPrint('Application for speaking is rejected');break;case RequestResultType.onCancelled:debugPrint('Application for speaking is canceled');break;case RequestResultType.onTimeout:debugPrint('Request to speak timeout');break;default:break;}} else {debugPrint('Error in requesting to speak');}
The TUILiveKit audience application for seat feature is mainly implemented through SeatGridView. You can call the following API Function to achieve the audience application for seat feature. The implementation is as follows, with Audience B applying for a seat as an example.
Note:
The room owner will only receive a request to speak when the room mode is APPLY_TO_TAKE, in FREE_TO_TAKE mode, takeSeat will directly succeed in taking the seat.
Host end receives a Request to Speak
Anchor A will receive Audience B's request to join the seat in the onSeatRequestReceived callback method.
override fun onSeatRequestReceived(type: VoiceRoomDefine.RequestType, userInfo: TUIRoomDefine.UserInfo) {if (type == VoiceRoomDefine.RequestType.APPLY_TO_TAKE_SEAT) {Log.i(TAG, "Received request to join the seat from the audience: ${userInfo.userId}")}}
@Overridepublic void onSeatRequestReceived(VoiceRoomDefine.RequestType type, TUIRoomDefine.UserInfo userInfo) {if (type == VoiceRoomDefine.RequestType.APPLY_TO_TAKE_SEAT) {Log.i(TAG, "Received request to join the mi from the audience: " + userInfo.userId);}}
import RTCRoomEngineimport LiveStreamCorefunc onSeatRequestReceived(type: SGRequestType, userInfo: TUIUserInfo) {if type == .applyToTakeSeat {print("Received audience's request to join the seat: \(userInfo.userId)")}}
import 'package:live_stream_core/live_stream_core.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';// Create a listening Observer instancefinal controller = SeatGridController();final exampleObserver = ExampleObserver();// Add an observercontroller.addObserver(exampleObserver);class ExampleObserver extends SeatGridWidgetObserver {ExampleObserver() {super.onSeatRequestReceived = (type, userInfo) {if (type == RequestType.applyToTakeSeat) {debugPrint('Receive an audience''s request to become a speaker: ${userInfo.userId}');}};}}
Note:
The room owner will only receive a request to take seat when the room mode is APPLY_TO_TAKE, in FREE_TO_TAKE mode, takeSeat will directly succeed in taking the seat.
Anchor responds to the request to take the seat
After Anchor A receives Audience B's request to join the seat, they can call responseRemoteRequest to respond whether they agree to let Audience B take seat. Audience B will receive a callback of Anchor A's acceptance or rejection (onAccepted/onRejected).
// Anchor agrees to let the audience take the seatseatGridView.responseRemoteRequest(userId, true, null);// Anchor rejects the audience's request to take the seatseatGridView.responseRemoteRequest(userId, false, null);
// Anchor agrees to let the audience take the seatseatGridView.responseRemoteRequest(userId, true, null);// Anchor rejects the audience's request to take the seatseatGridView.responseRemoteRequest(userId, false, null);
import RTCRoomEngineimport LiveStreamCore// Anchor agrees to let the audience take the seatseatGridView.responseRemoteRequest(userId, true) {} onError: { code, message in}// Anchor rejects the audience's request to take the seatseatGridView.responseRemoteRequest(userId, false) {} onError: { code, message in}
import 'package:live_stream_core/live_stream_core.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';final controller = SeatGridController();const String userId = 'Replace with the userId of userB';// Broadcaster agrees to let the audience become a speakerfinal result = await controller.responseRemoteRequest(userId, true);// Broadcaster denies audience to become a speakerfinal result = await controller.responseRemoteRequest(userId, false);
Callback for seat information changes
If you have already set up the Custom Seat View, you can listen to the updateSeatView callback to refresh your custom UI.
override fun updateSeatView(seatGridView: SeatGridView, seatInfo: TUIRoomDefine.SeatInfo, seatView: View) {Log.i(TAG, "Seat information changes");}
@Overridepublic void void updateSeatView(SeatGridView seatGridView, TUIRoomDefine.SeatInfo seatInfo, View seatView) {Log.i(TAG, "Seat information changes");}
If you have already set up the Custom Seat View, you can listen to the updateSeatView callback to refresh your custom seat UI.
import RTCRoomEngineimport LiveStreamCorefunc seatGridView(_ view: SeatGridView, updateSeatView seatInfo: TUISeatInfo, seatView: UIView) {print("Seat information changes")}
When the seat information changes, the corresponding value of the
seatInfoNotifier
parameter of SeatWidgetBuilder
will change. You can use your custom seat component as a child of ValueListenableBuilder
so that your custom seat UI can be updated instantly.// Definition of seatWidetBuildertypedef SeatWidgetBuilder = Widget Function(BuildContext context,ValueNotifier<TUISeatInfo> seatInfoNotifier,ValueNotifier<int> volumeNotifier);// Usage exampleimport 'package:live_stream_core/live_stream_core.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';SeatGridWidget(controller: controller,seatWidgetBuilder: (BuildContext context,ValueNotifier<TUISeatInfo> seatInfoNotifier,ValueNotifier<int> volumeNotifier) {return ValueListenableBuilder(valueListenable: seatInfoNotifier,builder: (context, seatInfo, _) {// Return your custom seat componentreturn Container();});})
Anchor invites audience to speak flow
Anchor sends invite speaking request
Anchor A sends an invite speaking request to Audience C. Audience C will receive the invite speaking request from Anchor A in the onSeatRequestReceived Callback.
val seatIndex = 1;val userId = "userIdC";val timeout = 60;seatGridView.takeUserOnSeatByAdmin(seatIndex, timeout, userId, object : VoiceRoomDefine.RequestCallback {override fun onAccepted(userInfo: TUIRoomDefine.UserInfo) {Log.i(TAG, "Invitation to Become a Speaker Accepted")}override fun onRejected(userInfo: TUIRoomDefine.UserInfo) {Log.i(TAG, "Invitation to Become a Speaker Rejected")}override fun onCancelled(userInfo: TUIRoomDefine.UserInfo) {Log.i(TAG, "Invitation to Become a Speaker Cancelled")}override fun onTimeout(userInfo: TUIRoomDefine.UserInfo) {Log.i(TAG, "Invitation to Become a Speaker Timed Out")}override fun onError(userInfo: TUIRoomDefine.UserInfo, error: TUICommonDefine.Error, message: String) {Log.i(TAG, "Invitation to Become a Speaker Error")}})
val seatIndex = 1;val userId = "userIdC";val timeout = 60;seatGridView.takeUserOnSeatByAdmin(seatIndex, userId, timeout, new VoiceRoomDefine.RequestCallback() {@Overridepublic void onAccepted(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "Invitation to Become a Speaker Accepted");}@Overridepublic void onRejected(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "Invitation to Become a Speaker Rejected");}@Overridepublic void onCancelled(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "Invitation to Become a Speaker Cancelled");}@Overridepublic void onTimeout(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "Invitation to Become a Speaker Timed Out");}@Overridepublic void onError(TUIRoomDefine.UserInfo userInfo, TUICommonDefine.Error error, String message) {Log.i(TAG, "Invitation to Become a Speaker Error");}});
import RTCRoomEngineimport LiveStreamCorelet seatIndex = 1let userId = "userIdC"seatGridView.takeUserOnSeatByAdmin(index: seatIndex, timeout: timeout, userId: userId) { userInfo inprint("Invitation to join the microphone is approved")} onRejected: { userInfo inprint("Invitation to join the microphone is rejected")} onCancelled: { userInfo inprint("Invitation to join the microphone is canceled")} onTimeout: { userInfo inprint("Invitation to join the microphone times out")} onError: { userInfo, code, message inprint("Invitation to join the microphone error")}
import 'package:live_stream_core/live_stream_core.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';const int seatIndex = 1;const String userId = 'userIdC'const int timeout = 60;final controller = SeatGridController();final result = await controller.takeUserOnSeatByAdmin(seatIndex, userId, timeout);if (result.code == TUIError.success) {switch (result.type) {case RequestResultType.onAccepted:debugPrint('Invitation to become a speaker accepted');break;case RequestResultType.onRejected:debugPrint('Invitation to become a speaker rejected');break;case RequestResultType.onCancelled:debugPrint('Invitation to become a speaker cancelled');break;case RequestResultType.onTimeout:debugPrint('Invitation to become a speaker timed out');break;default:break;}} else {debugPrint('Error in invitation to become a speaker');}
Audience Side received an Invite Speaking Request
Audience C will receive Anchor A's invite to join the seat in the onSeatRequestReceived callback method.
override fun onSeatRequestReceived(type: VoiceRoomDefine.RequestType, userInfo: TUIRoomDefine.UserInfo) {if (type == VoiceRoomDefine.RequestType.INVITE_TO_TAKE_SEAT) {Log.i(TAG, "Received invite to join the seat from the anchor: ${userInfo.userId}")}}
@Overridepublic void onSeatRequestReceived(VoiceRoomDefine.RequestType type, TUIRoomDefine.UserInfo userInfo) {if (type == VoiceRoomDefine.RequestType.INVITE_TO_TAKE_SEAT) {Log.i(TAG, "Received invite to join the seat from the anchor: " + userInfo.userId);}}
import RTCRoomEngineimport LiveStreamCorefunc onSeatRequestReceived(type: SGRequestType, userInfo: TUIUserInfo) {if type == .inviteToTakeSeat {print("Received invite to join the seat from the anchor: \(userInfo.userId)")}}
import 'package:live_stream_core/live_stream_core.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';// Create a listening Observer instancefinal controller = SeatGridController();final exampleObserver = ExampleObserver();// Add an observercontroller.addObserver(exampleObserver);class ExampleObserver extends SeatGridWidgetObserver {ExampleObserver() {super.onSeatRequestReceived = (type, userInfo) {if (type == RequestType.inviteToTakeSeat) {debugPrint('Receive an invitation to become a speaker from the anchor: ${userInfo.userId}');}};}}
Audience responds to the invite to speak
After Audience C receives the join request from Anchor A, they can call responseRemoteRequest to respond whether they agree to speak. Anchor A will receive Audience C's acceptance or rejection (onAccepted/onRejected) callback.
// Audience agrees to the anchor's inviteseatGridView.responseRemoteRequest("", true, null);// Audience rejects the anchor's inviteseatGridView.responseRemoteRequest("", false, null);
// Audience agrees to the anchor's inviteseatGridView.responseRemoteRequest("", true, null);// Audience rejects the anchor's inviteseatGridView.responseRemoteRequest("", false, null);
import RTCRoomEngineimport LiveStreamCore// Audience agrees to the anchor's inviteseatGridView.responseRemoteRequest("userId of anchor", true) {} onError: { code, message in}// Audience rejects the anchor's inviteseatGridView.responseRemoteRequest("userId of anchor", false, null) {} onError: { code, message in}
import 'package:live_stream_core/live_stream_core.dart';final controller = SeatGridController();const String userId = 'userId of anchor';// Audience accepts the anchor's invitationfinal result = await controller.responseRemoteRequest(userId, true);// Audience rejects the anchor's invitationfinal result = await controller.responseRemoteRequest(userId, false);
Note:
If using a custom seat view, both anchors and the audience can refresh the seat UI by listening to the callback for seat information changes.
Leave the Seat Process
After the audience successfully becomes a speaker, they proactively become a listener again.
After Audience B becomes a speaker successfully, they can call leaveSeat to leave the mic voluntarily.
seatGridView.leaveSeat()
seatGridView.leaveSeat();
import RTCRoomEngineimport LiveStreamCoreseatGridView.leaveSeat() {} onError: { code, message in}
import 'package:live_stream_core/live_stream_core.dart';final controller = SeatGridController();final result = await controller.leaveSeat();
After the audience successfully joins the stage, the host kicks them off stage
After Audience B becomes a speaker successfully, Anchor A can kick Audience B off.
val userId = "userIdB"seatGridView.kickUserOffSeatByAdmin(userId, null)
String userId = "userIdB";seatGridView.kickUserOffSeatByAdmin(userId, null);
import RTCRoomEngineimport LiveStreamCorelet userId = "userIdB"seatGridView.kickUserOffSeatByAdmin(userId) {} onError: { code, message in}
import 'package:live_stream_core/live_stream_core.dart';const String userId = "userIdB";final controller = SeatGridController();final result = await controller.kickUserOffSeatByAdmin();
Audience receives a callback when the host kicks them off stage
After Anchor A kicks Audience B off, Audience B will receive the onKickedOffSeat callback.
override fun onKickedOffSeat(inviterUser: UserInfo) {Log.i(TAG, "Anchor kicked off the seat")}
@Overridepublic void onKickedOffSeat(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "Anchor kicked off the seat");}
import RTCRoomEngineimport LiveStreamCorefunc onKickedOffSeat(userInfo: TUIUserInfo) {print("Anchor kicked off the seat")}
import 'package:live_stream_core/live_stream_core.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';// Create a listening Observer instancefinal controller = SeatGridController();final exampleObserver = ExampleObserver();// Add an observercontroller.addObserver(exampleObserver);class ExampleObserver extends SeatGridWidgetObserver {ExampleObserver() {super.onKickedOffSeat = (userInfo) {debugPrint('Anchor kicks off the mic');};}}
Note:
If using a custom seat view, both anchors and the audience can refresh the seat UI by listening to the callback for seat information changes.
Lock Seat Process
Position Lock
The anchor can lock a specific seat, and it will be blocked, preventing any seat operations.
val index = 1;val isLockSeat = trueval params = TUIRoomDefine.SeatLockParams().apply {lockSeat = isLockSeat}seatGridView.lockSeat(index, params, null)
int index = 1;bool isLockSeat = true;TUIRoomDefine.SeatLockParams params = new TUIRoomDefine.SeatLockParams();params.lockSeat = isLockSeat;seatGridView.lockSeat(index, params, null);
import RTCRoomEngineimport LiveStreamCorelet index = 1let isLockSeat = truelet params = TUISeatLockParams()params.lockSeat = isLockSeatseatGridView.lockSeat(index: index, lockMode: params) {} onError: { code, message in}
import 'package:live_stream_core/live_stream_core.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';const int index = 1;const bool isLockSeat = truefinal params = TUISeatLockParams();params.lockSeat = isLockSeat;final controller = SeatGridController();final result = await controller.lockSeat(index, params);
Audio Lock
The anchor can lock the audio for a specific seat position, and the user on that position will be muted.
val index = 1;bool isAudioLocked = true;val params = TUIRoomDefine.SeatLockParams().apply {isAudioLocked = isLockSeat}seatGridView.lockSeat(index, params, null)
int index = 1;bool isAudioLocked = true;TUIRoomDefine.SeatLockParams params = new TUIRoomDefine.SeatLockParams();params.lockAudio = seatInfo.isAudioLocked;seatGridView.lockSeat(index, params, null);
import RTCRoomEngineimport LiveStreamCorelet index = 1let isAudioLocked = truelet params = TUISeatLockParams()params.lockAudio = isAudioLockedseatGridView.lockSeat(index: index, lockMode: params) {} onError: { code, message in}
import 'package:live_stream_core/live_stream_core.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';const int index = 1;const bool isAudioLocked = truefinal params = TUISeatLockParams();params.lockAudio = isAudioLocked;final controller = SeatGridController();final result = await controller.lockSeat(index, params);
Note:
If using a custom seat view, both anchors and the audience can refresh the seat UI by listening to the callback for seat information changes.
Other seat management feature extensions
The seat management feature is implemented based on SeatGridView. If you need to extend the seat management feature, please refer to the SeatGridView documentation.
The seat management feature is implemented based on SeatGridView. If you need to extend the seat management feature, please refer to the SeatGridView documentation.
The seat management feature is implemented based on SeatGridView. If you need to extend the seat management feature, please refer to the SeatGridWidget documentation.