Platform | Version |
Flutter | Flutter 3.27.4 and higher versions. Dart 3.6.2 or higher |
Android | Android Studio 3.5 and above versions. Android devices with Android 5.0 and above. |
iOS | Xcode 15.0 or higher. Ensure that your project has a deemed valid developer signature. |
build.gradle.kts (or build.gradle) file, add the following code in it, and add a dependency on the SeatGridView Component:api("io.trtc.uikit:live-stream-core:latest.release")
api 'io.trtc.uikit:live-stream-core:latest.release'
proguard-rules.pro file:-keep class com.tencent.** { *; }-keep class com.trtc.uikit.livekit.voiceroomcore.** { *; }-keep class com.google.gson.** { *;}
AndroidManifest.xml file. In the application node, add tools:replace="android:allowBackup" and android:allowBackup="false". Override the settings within the component and use your own settings.// app/src/main/AndroidManifest.xml<application...// Add the following configuration to overwrite the configuration in the dependent sdk.android:allowBackup="false"tools:replace="android:allowBackup">
pod 'LiveStreamCore' dependency in your Podfile.target 'xxxx' do......pod 'LiveStreamCore'end
Podfile file, first use the terminal to cd into the xxxx.xcodeproj directory, then create it through the following command:pod init
cd to the Podfile directory, then execute the following commands to install components.pod install
pod repo update
pod update
flutter pubaddlive_stream_core
TUIRoomEngine.login(applicationContext,1400000001, // Replace with the SDKAppID obtained in step 1"denny" // Replace with your UserID"xxxxxxxxxxx", // You can calculate a UserSig in the console and fill it in this positionobject : TUIRoomDefine.ActionCallback() {override fun onSuccess() {Log.i(TAG, "login success")}override fun onError(errorCode: Int, errorMessage: String) {Log.e(TAG, "login failed, errorCode: $errorCode msg:$errorMessage")}})
TUIRoomEngine.login(context,1400000001, // Replace with the SDKAppID obtained in step 1"denny" // Replace with your UserID"xxxxxxxxxxx", // You can calculate a UserSig in the console and fill it in this positionnew TUIRoomDefine.ActionCallback() {@Overridepublic void onSuccess() {Log.i(TAG, "login success");}@Overridepublic void onError(TUICommonDefine.Error error, String message) {Log.e(TAG, "login failed, errorCode: " + errorCode + " msg:" + errorMessage);}});
//// AppDelegate.swift//import RTCRoomEnginefunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {TUIRoomEngine.login(sdkAppId: 1400000001, // Replace with the SDKAppID obtained in step 1userId: "denny", // Replace with your UserIDuserSig: "xxxxxxxxxxx") { // You can calculate a UserSig in the console and fill it in this positionprint("login success")} onError: { code, message inprint("login failed, code: \\(code), error: \\(message ?? "nil")")}return true}
final result = await TUIRoomEngine.login('Replace with your activated SDKAppID','Replace with your userId','Replace with your userSig');
Parameters | Type | Overview |
SDKAppID | int | In the final step of step 1, you have already obtained it. No redundancy here. |
UserID | String | Current user's ID, string type, only allow to include English letters (a-z and A-Z), digits (0-9), hyphen and underscore. |
userSig | String | Obtain the SecretKey from step 3 of Step 1, use it to encrypt information such as SDKAppID and UserID, and you can get UserSig. It is an authentication token used by Tencent Cloud to verify whether the current user can use the TRTC service. You can generate a temporary available UserSig through the Auxiliary Tool in the console. For more information, see How to Calculate and Use UserSig. |
GenerateTestUserSig.genTestSig function to generate userSig. In this method, SDKSecretKey can be easily decompiled and reverse cracked. Once your key is leaked, attackers can misappropriate your Tencent Cloud traffic.Room owner enables voice chat room & becomes speaker |
![]() |
val seatGridView = SeatGridView(this)
SeatGridView seatGridView = new SeatGridView(this);
import LiveStreamCorelet seatGridView = SeatGridView()
SeatGridController, then assign it to the core component of the voice chat room SeatGridWidget.SeatGridController is responsible for providing APIs. SeatGridWidget is used to display the seat UI. You can add SeatGridWidget anywhere you need to display the seat UI.final controller = SeatGridController();SeatGridWidget(controller: controller);
val roomInfo = TUIRoomDefine.RoomInfo()roomInfo.roomId = Config.roomIdroomInfo.seatMode = TUIRoomDefine.SeatMode.APPLY_TO_TAKEseatGridView.startVoiceRoom(roomInfo, object : TUIRoomDefine.GetRoomInfoCallback {override fun onSuccess(roomInfo: TUIRoomDefine.RoomInfo) {val seatIndex = 1val timeout = 60seatGridView.takeSeat(seatIndex, timeout, null)seatGridView.startMicrophone(null)}override fun onError(error: TUICommonDefine.Error, message: String) {}})
TUIRoomDefine.RoomInfo roomInfo = new TUIRoomDefine.RoomInfo();roomInfo.roomId = "roomId_123456";roomInfo.seatMode = TUIRoomDefine.SeatMode.APPLY_TO_TAKE;seatGridView.startVoiceRoom(roomInfo, new TUIRoomDefine.GetRoomInfoCallback() {@Overridepublic void onSuccess(TUIRoomDefine.RoomInfo roomInfo) {int seatIndex = 1;int timeout = 60;seatGridView.takeSeat(seatIndex, timeout, null);seatGridView.startMicrophone(null);}@Overridepublic void onError(TUICommonDefine.Error error, String message) {}});
import LiveStreamCoreimport RTCRoomEnginelet roomInfo = TUIRoomInfo()roomInfo.roomId = "123456"roomInfo.seatMode = .applyToTakeseatGridView.startVoiceRoom(roomInfo: roomInfo) { roomInfo in} onError: { code, message in}seatGridView.startMicrophone() {} onError: { code,message in}
import 'package:live_stream_core/live_stream_core.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';final roomInfo = TUIRoomInfo(roomId: 'replace with your roomId');roomInfo.name = 'replace with your roomName';roomInfo.seatMode = TUISeatMode.applyToTake;roomInfo.isSeatEnabled = true;roomInfo.roomType = TUIRoomType.livingRoom;final startVoiceRoomResult = await controller.startVoiceRoom(roomInfo);final startMicrophoneResult = await controller.startMicrophone();
Audience joins a voice chat room |
![]() |
seatGridView.joinVoiceRoom("roomId_123456", null)
seatGridView.joinVoiceRoom("roomId_123456", null);
import LiveStreamCoreimport RTCRoomEngineseatGridView.joinVoiceRoom(roomId: "roomId_123456") { roomInfo in} onError: { code, message in}
import 'package:live_stream_core/live_stream_core.dart';final result = await controller.joinVoiceRoom('replace with your roomId');
Apply for Microphone Mode Before | Become a Speaker |
![]() | ![]() |
SeatGridView (Android & iOS) / SeatGridController (Flutter). You can call the following API functions to implement the audience apply for microphone mode feature. The implementation is as follows, taking Audience B's request to speak as an example.val seatIndex = 1;val timeout = 60;seatGridView.takeSeat(seatIndex, timeout, object : VoiceRoomDefine.RequestCallback {override fun onAccepted(userInfo: TUIRoomDefine.UserInfo) {Log.i(TAG, "Request to speak 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, "Request to speak timed out")}override fun onError(userInfo: TUIRoomDefine.UserInfo, error: TUICommonDefine.Error, message: String) {Log.i(TAG, "Error in applying for speaking")}})
int seatIndex = 1;int timeout = 60;seatGridView.takeSeat(seatIndex, timeout, new VoiceRoomDefine.RequestCallback() {@Overridepublic void onAccepted(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "Request to speak 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, "Request to speak timed out")}@Overridepublic void onError(TUIRoomDefine.UserInfo userInfo, TUICommonDefine.Error error, String message) {Log.i(TAG, "Error in requesting to speak")}});
import RTCRoomEngineimport LiveStreamCorelet seatIndex = 1let timeout = 60seatGridView.takeSeat(index: index, timeout: timeout) { userInfo inprint("Request to speak approved")} onRejected: { userInfo inprint("Application for speaking is rejected")} onCancelled: { userInfo inprint("Application for speaking is canceled")} onTimeout: { userInfo inprint("Request to speak timed out")} onError: { userInfo, code, message inprint("Error in applying for speaking")}
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 timed out');break;default:break;}} else {debugPrint('Error in applying for speaking');}
SeatGridView (Android & iOS) / SeatGridController (Flutter). You can call the following API functions to implement the audience's request to speak feature. The implementation is as follows, taking Audience B's request to speak as an example.// Broadcaster agrees to let the audience speakseatGridView.responseRemoteRequest(userId, true, null);// Broadcaster denies the audience's speaking requestseatGridView.responseRemoteRequest(userId, false, null);
// Broadcaster agrees to let the audience speakseatGridView.responseRemoteRequest(userId, true, null);// Broadcaster rejects the audience's speaking requestseatGridView.responseRemoteRequest(userId, false, null);
import RTCRoomEngineimport LiveStreamCore// Broadcaster agrees to let the audience speakseatGridView.responseRemoteRequest(userId, true) {} onError: { code, message in}// Broadcaster denies the audience's speaking requestseatGridView.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 speakfinal result = await controller.responseRemoteRequest(userId, true);// Broadcaster denies the audience's speaking requestfinal result = await controller.responseRemoteRequest(userId, false);
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")}
import RTCRoomEngineimport LiveStreamCorefunc seatGridView(_ view: SeatGridView, updateSeatView seatInfo: TUISeatInfo, seatView: UIView) {print("Seat information changes")}
seatInfoNotifier parameter in SeatWidgetBuilder will change. You can use your custom seat widget as a child component of ValueListenableBuilder to update your custom seat UI 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();});})
Before Moving Microphone Positions | After Moving Microphone Positions |
![]() | ![]() |
val index = 3seatGridView.moveToSeat(index, object : TUIRoomDefine.ActionCallback {override fun onSuccess() {// Move seat successfully}override fun onError(error: TUICommonDefine.Error, message: String) {// Failed to move seat}})
int index = 3;seatGridView.moveToSeat(index, new TUIRoomDefine.ActionCallback() {@Overridepublic void onSuccess() {// Move seat successfully}@Overridepublic void onError(TUICommonDefine.Error error, String message) {// Failed to move seat}});
import RTCRoomEngineimport LiveStreamCorelet index = 3self.seatGridView.moveToSeat(index: destinationIndex) {// Move seat successfully} onError: { code, message in// Failed to move seat}
import 'package:live_stream_core/live_stream_core.dart';final destinationIndex = 3;final result = await controller.moveToSeat(destinationIndex);
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();
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 timeout = 60let userId = "userIdC"seatGridView.takeUserOnSeatByAdmin(index: seatIndex, timeout: timeout, userId: userId) { userInfo inprint("Invitation to become a speaker accepted")} onRejected: { userInfo inprint("Invitation to become a speaker rejected")} onCancelled: { userInfo inprint("Invitation to become a speaker cancelled")} onTimeout: { userInfo inprint("Invitation to become a speaker timed out")} onError: { userInfo, code, message inprint("Invitation to become a speaker 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 inviting to become a speaker');}
override fun onSeatRequestReceived(type: VoiceRoomDefine.RequestType, userInfo: TUIRoomDefine.UserInfo) {if (type == VoiceRoomDefine.RequestType.INVITE_TO_TAKE_SEAT) {Log.i(TAG, "Receive an invitation to become a speaker 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, "Receive an invitation to become a speaker from the anchor: " + userInfo.userId);}}
import RTCRoomEngineimport LiveStreamCorefunc onSeatRequestReceived(type: SGRequestType, userInfo: TUIUserInfo) {if type == .inviteToTakeSeat {print("Receive an invitation to become a speaker 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 listener Observer instancefinal controller = SeatGridController();final exampleObserver = ExampleObserver();// Add 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 accepts the anchor's invitationseatGridView.responseRemoteRequest("", true, null);// Audience rejects the anchor's invitationseatGridView.responseRemoteRequest("", false, null);
// Audience accepts the anchor's invitationseatGridView.responseRemoteRequest("", true, null);// The audience rejects the anchor's invitationseatGridView.responseRemoteRequest("", false, null);
import RTCRoomEngineimport LiveStreamCore// Audience accepts the anchor's invitationseatGridView.responseRemoteRequest("userId of anchor", true) {} onError: { code, message in}// Audience rejects the anchor's invitationseatGridView.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);// The audience rejects the anchor's invitationfinal result = await controller.responseRemoteRequest(userId, false);
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();
override fun onKickedOffSeat(inviterUser: UserInfo) {Log.i(TAG, "Anchor removes speaker")}
@Overridepublic void onKickedOffSeat(TUIRoomDefine.UserInfo userInfo) {Log.i(TAG, "Anchor removes speaker from mic");}
import RTCRoomEngineimport LiveStreamCorefunc onKickedOffSeat(userInfo: TUIUserInfo) {print("Anchor removes speaker from mic")}
import 'package:live_stream_core/live_stream_core.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';// Create a listener Observer instancefinal controller = SeatGridController();final exampleObserver = ExampleObserver();// Add observercontroller.addObserver(exampleObserver);class ExampleObserver extends SeatGridWidgetObserver {ExampleObserver() {super.onKickedOffSeat = (userInfo) {debugPrint('Anchor kicks the speaker off the mic');};}}
Before Locking Seats | After Locking Seats |
![]() | ![]() |
val index = 1val 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);
Grid layout | Element layout | Vertical layout | Custom layout |
![]() | ![]() |
![]() |
![]() |
// Set grid layoutseatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.GRID, null)// Set element layoutseatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.FOCUS, null)// Set vertical layoutseatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.VERTICAL, null)// Set custom layoutval layoutConfig = VoiceRoomDefine.SeatViewLayoutConfig().apply {rowConfigs = ArrayList()rowSpacing = dp2px(10); // Spacing between each line}// First line configurationval rowConfig1 = VoiceRoomDefine.SeatViewLayoutRowConfig().apply {count = 3 // Number displayed in the first lineseatSize = VoiceRoomDefine.Size(dp2px(50), dp2px(50)) // Size of each microphone position view displayed in the first rowseatSpacing = dp2px(10) // Horizontal spacing between each seat in the first rowalignment = VoiceRoomDefine.SeatViewLayoutRowAlignment.CENTER // Alignment mode of seats in the first row}layoutConfig.rowConfigs.add(rowConfig1)// Second line configurationval rowConfig2 = VoiceRoomDefine.SeatViewLayoutRowConfig().apply {count = 3 // Number displayed in the second lineseatSize = VoiceRoomDefine.Size(dp2px(50), dp2px(50)) // Size of each seat displayed in the second rowseatSpacing = dp2px(10) // Horizontal spacing between each seat in the second rowalignment = VoiceRoomDefine.SeatViewLayoutRowAlignment.SPACE_AROUND // Alignment mode of seats in the second row}layoutConfig.rowConfigs.add(rowConfig2)seatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.FREE, layoutConfig)
// Set grid layoutseatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.GRID, null)// Set element layoutseatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.FOCUS, null)// Set vertical layoutseatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.VERTICAL, null)// Set free layoutVoiceRoomDefine.SeatViewLayoutConfig layoutConfig = new VoiceRoomDefine.SeatViewLayoutConfig();layoutConfig.rowConfigs = new ArrayList<>();layoutConfig.rowSpacing = dp2px(10); // Spacing between each line// First line configurationVoiceRoomDefine.SeatViewLayoutRowConfig rowConfig1 = new VoiceRoomDefine.SeatViewLayoutRowConfig();rowConfig1.count = 3; // Number displayed in the first linerowConfig1.seatSize = new VoiceRoomDefine.Size(dp2px(50), dp2px(50)); // Size of each microphone position view displayed in the first rowrowConfig1.seatSpacing = dp2px(10); // Horizontal spacing between each seat in the first rowrowConfig1.alignment = VoiceRoomDefine.SeatViewLayoutRowAlignment.CENTER; // Alignment mode of seats in the first rowlayoutConfig.rowConfigs.add(rowConfig1);// Second line configurationVoiceRoomDefine.SeatViewLayoutRowConfig rowConfig2 = new VoiceRoomDefine.SeatViewLayoutRowConfig();rowConfig2.count = 3; // Number displayed in the second rowrowConfig2.seatSize = new VoiceRoomDefine.Size(dp2px(50), dp2px(50)); // Size of each microphone position view displayed in the second rowrowConfig1.seatSpacing = dp2px(10); // Horizontal spacing between each seat in the second rowrowConfig2.alignment = VoiceRoomDefine.SeatViewLayoutRowAlignment.SPACE_AROUND; // Alignment mode of seats in the second rowlayoutConfig.rowConfigs.add(rowConfig2);seatGirdView.setLayoutMode(VoiceRoomDefine.LayoutMode.FREE, layoutConfig);
import LiveStreamCore// Set grid layoutseatGridView.setLayoutMode(layoutMode: .grid)// Set element layoutseatGridView.setLayoutMode(layoutMode: .focus)// Set vertical layoutseatGridView.setLayoutMode(layoutMode: .vertical)// Set custom layout// First line configurationlet rowConfig1 = SGSeatViewLayoutRowConfig(count: 3, // Number displayed in the first lineseatSpacing: 10, // Horizontal spacing between each seat in the first rowseatSize: CGSize(width: 50, height: 50), // Size of each microphone position view displayed in the first rowalignment: .center) // Alignment mode of seats in the first row// Second line configurationlet rowConfig2 = SGSeatViewLayoutRowConfig(count: 3, // Number displayed in the second rowseatSpacing: 10, // Horizontal spacing between each seat in the second rowseatSize: CGSize(width: 50, height: 50), // Size of each microphone position view displayed in the second rowalignment: .spaceAround) // Alignment mode of microphone positions in the second rowlet layoutConfig = SGSeatViewLayoutConfig(rowConfigs: [rowConfig1, rowConfig2],rowSpacing: 10)seatGridView.setLayoutMode(layoutMode: .free, layoutConfig: layoutConfig)
// API definitionvoid setLayoutMode(LayoutMode layoutMode, SeatWidgetLayoutConfig? layoutConfig);// API utilizationimport 'package:live_stream_core/live_stream_core.dart';// Set grid layoutcontroller.setLayoutMode(LayoutMode.grid, null);// Set element layoutcontroller.setLayoutMode(LayoutMode.focus, null);// Set vertical layoutcontroller.setLayoutMode(LayoutMode.vertical, null);// Set custom layoutfinal rowConfig = SeatWidgetLayoutRowConfig(count: 2,seatSpacing: 20.0,seatSize: const Size(80, 80),alignment: SeatWidgetLayoutRowAlignment.spaceBetween);final layoutConfig = SeatWidgetLayoutConfig(rowConfigs: [rowConfig, rowConfig]);controller.setLayoutMode(LayoutMode.free, layoutConfig);
Diagram of custom layout alignment mode |
![]() |
val adapter = object : VoiceRoomDefine.SeatViewAdapter {override fun createSeatView(seatGridView: SeatGridView, seatInfo: TUIRoomDefine.SeatInfo): View {return TestSeatInfoView(context, seatGridView, seatInfo)}override fun updateSeatView(seatGridView: SeatGridView,seatInfo: TUIRoomDefine.SeatInfo, seatView: View) {(seatView as TestSeatInfoView).updateSeatView(seatGridView, seatInfo)}override fun updateUserVolume(seatGridView: SeatGridView, volume: Int, customSeatView: View) {(customSeatView as TestSeatInfoView).updateUserVolume(seatGridView, volume)}}seatGirdView.setSeatViewAdapter(adapter)class TestSeatInfoView constructor(context: Context, seatGirdView: SeatGridView, seatInfo: TUIRoomDefine.SeatInfo) : FrameLayout(context) {init {initializeView()}fun updateSeatView(seatGirdView: SeatGridView, seatInfo: TUIRoomDefine.SeatInfo) {updateView(seatInfo) // Refresh the custom seat view UI}fun updateUserVolume(seatGirdView: SeatGridView, volume: Int) {updateUserVolume(volume) // Refresh the volume change UI}}
VoiceRoomDefine.SeatViewAdapter adapter = new VoiceRoomDefine.SeatViewAdapter() {@Overridepublic View createSeatView(SeatGridView seatGridView, TUIRoomDefine.SeatInfo seatInfo) {return new TestSeatInfoView(getApplicationContext(), seatGridView, seatInfo);}@Overridepublic void updateSeatView(SeatGridView seatGridView, TUIRoomDefine.SeatInfo seatInfo,View customSeatView) {((TestSeatInfoView) customSeatView).updateSeatView(seatGridView, seatInfo);}@Overridepublic void updateUserVolume(SeatGridView seatGridView, int volume, View customSeatView) {((TestSeatInfoView) customSeatView).updateUserVolume(seatGridView, volume);}};seatGirdView.setSeatViewAdapter(adapter);public class TestSeatInfoView extends FrameLayout {public TestSeatInfoView(@NonNull Context context, SeatGridView seatGirdView, TUIRoomDefine.SeatInfo seatInfo) {super(context);initView();}public void updateSeatView(SeatGridView seatGirdView, TUIRoomDefine.SeatInfo seatInfo) {updateView(seatInfo);}public void updateUserVolume(SeatGridView seatGirdView, int volume) {updateUserVolume(volume);}}
import LiveStreamCoreclass TestSeatViewDelegate: SGSeatViewDelegate {func seatGridView(_ view: SeatGridView, createSeatView seatInfo: TUISeatInfo) -> UIView? {return TestSeatInfoView(seatGridView: view, seatInfo: seatInfo)}func seatGridView(_ view: SeatGridView, updateSeatView seatInfo: TUISeatInfo, seatView: UIView) {if let seatView = seatView as? TestSeatInfoView {seatView.updateSeatView(seatGridView: view, seatInfo: seatInfo)}}func seatGridView(_ view: SeatGridView, updateUserVolume volume: Int, seatView: UIView) {if let seatView = seatView as? TestSeatInfoView {seatView.updateUserVolume(seatGridView: view, volume: volume)}}}seatGridView.setSeatViewDelegate(TestSeatViewDelegate())class TestSeatInfoView: UIView {init(seatGridView: SeatGridView, seatInfo: TUISeatInfo) {super.init(frame: .zero)initializeView()}func updateSeatView(seatGridView: SeatGridView, seatInfo: TUISeatInfo) {updateView(seatInfo) // Refresh the custom seat view UI}func updateUserVolume(seatGridView: SeatGridView, volume: Int) {updateUserVolume(volume) // Refresh the volume change UI}}
seatWidetBuilder of SeatGridWidget.// 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,onSeatWidgetTap: (TUISeatInfo seatInfo) {// debugPrint('click seatWidget index:${seatInfo.index}');},seatWidgetBuilder: (BuildContext context,ValueNotifier<TUISeatInfo> seatInfoNotifier,ValueNotifier<int> volumeNotifier) {// Return your custom seat componentreturn Container();})
Default Microphone Position View | Custom Seat View Example |
![]() |
![]() |
Feedback