LiveListStore and LiveSeatStore.LiveSeatStore:Core Concepts | Type | Core Responsibilities and Description |
class | The core of seat management is responsible for managing ALL seat information and related operations in the room. Expose the real-time microphone position list data stream through liveSeatState.seatList. | |
class | Represent the current status of the microphone position. seatList is a ValueListenable<List<SeatInfo>> type that stores the real-time status of the seat position list;speakingUsers signifies the person currently speaking and the corresponding volume. | |
class | The data model of one microphone position. The seat position list ( seatList) pushed by LiveSeatStore consists of multiple SeatInfo objects. Key field:index (the index of the seat position).isLocked (whether the seat position is locked).userInfo (user information on the seat position. If the seat position is empty, this field is also an empty object). | |
class | The detailed data model of the on-mic user. When a user successfully gets on mic, the userInfo field in SeatInfo will be filled with this user. Key field:userID (user's unique ID)userName (user's nickname)avatarURL (user's profile photo URL)microphoneStatus (Mic status)cameraStatus (Camera status). |
SDKAppID: Application identifier (required). Tencent Cloud uses SDKAppID for billing and details.SDKSecretKey: Application secret key, used to initialize the configuration file with secret information.
atomic_x_core dependency in your pubspec.yaml file, then execute flutter pub get.dependencies:atomic_x_core: ^3.6.0
android/app/src/main/AndroidManifest.xml.<uses-permission android:name="android.permission.RECORD_AUDIO" />
ios directory Podfile and ios/Runner directory Info.plist.post_install do |installer|installer.pods_project.targets.each do |target|flutter_additional_ios_build_settings(target)target.build_configurations.each do |config|config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)','PERMISSION_MICROPHONE=1',]endendend
<key>NSMicrophoneUsageDescription</key><string>TUILiveKit needs to access your microphone permission. Recorded video will have sound when enabled</string>

LoginStore.shared.login in the project to complete login. This is the key premise for using AtomicXCore all functions.import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';// main.dartvoid main() async {WidgetsFlutterBinding.ensureInitialized();// Log in.final result = await LoginStore.shared.login(sdkAppID: 1400000001, // Replace with your sdkAppIDuserID: "test_001", // Replace with your user IDuserSig: "xxxxxxxxxxx", // Replace with your userSig);if (result.isSuccess) {debugPrint("login success");} else {debugPrint("login failed code: ${result.code}, message: ${result.message}");}runApp(const MyApp());}
Parameter | Type | Description |
sdkAppID | Int | |
userID | String | The unique ID for the current user. Must contain only English letters, numbers, hyphens, and underscores. |
userSig | String | A credential for TRTC authentication: Development Environment: You can use the local GenerateTestUserSig.genTestSig function to generate a UserSig or generate a temporary UserSig via the UserSig Generation Tool.Production Environment: To prevent key leakage, you must generate UserSig on the server side. For details, see Generating UserSig on the Server. For more information, see How to Calculate and Use UserSig. |
LiveSeatStore instance. You need to listen to changes in liveSeatStore.liveSeatState for real-time seat updates and UI rendering.import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';// YourAnchorPage represents your host homepageclass YourAnchorPage extends StatefulWidget {const YourAnchorPage({super.key});@overrideState<YourAnchorPage> createState() => _YourAnchorPageState();}class _YourAnchorPageState extends State<YourAnchorPage> {final _liveListStore = LiveListStore.shared;final _deviceStore = DeviceStore.shared;// Initialize LiveSeatStore with liveIDfinal String _liveID = "test_voice_room_001";late final LiveSeatStore _liveSeatStore;late final VoidCallback _seatListListener = _onSeatListChanged;@overridevoid initState() {super.initState();_liveSeatStore = LiveSeatStore.create(_liveID);// Listen to microphone position list adjustment_observeSeatList();}void _observeSeatList() {// Listen to changes in liveSeatState.seatList and refresh your mic seat UI_liveSeatStore.liveSeatState.seatList.addListener(_seatListListener);}void _onSeatListChanged() {// Render your mic seat UI here based on seatInfoListfinal seatInfoList = _liveSeatStore.liveSeatState.seatList.value;debugPrint("Seat list updated: ${seatInfoList.length} seats");setState(() {});}@overridevoid dispose() {_liveSeatStore.liveSeatState.seatList.removeListener(_seatListListener);super.dispose();}@overrideWidget build(BuildContext context) {// Assume that you have your own layoutreturn Container();}}
DeviceStore openLocalMicrophone API:class _YourAnchorPageState extends State<YourAnchorPage> {// ... Other code ...void _openDevices() {// Turn the mic onDeviceStore.shared.openLocalMicrophone();}}
LiveListStore createLive API to start a voice chat room live stream:class _YourAnchorPageState extends State<YourAnchorPage> {// ... Other code ...final String _liveID = "test_voice_room_001";@overridevoid initState() {super.initState();// ... Other code ...// Start Voice Chat_startLive();}Future<void> _startLive() async {// 1. Prepare the LiveInfo objectfinal liveInfo = LiveInfo(// 2. Set the room idliveID: _liveID,// 3. Set the room nameliveName: "test voice chat room",// 4. Configured as a voice chat room (enable seat)isSeatEnabled: true,// 5. Room owner on seat by defaultkeepOwnerOnSeat: true,// 6. Set the seat layout template (for example, 70 is the 10-seat template)// Important: According to the product specification, input the correct IDseatLayoutTemplateID: 70,// 7. Set the microphone mode, such as apply for microphone modeseatMode: TakeSeatMode.apply,// 8. Set the maximum number of microphone slotsmaxSeatCount: 10,);// 9. Call createLive to start live streamingfinal result = await _liveListStore.createLive(liveInfo);if (result.isSuccess) {debugPrint("Response startLive onSuccess");// Once created successfully, the room owner will join the stage by default. At this point, you can call unmuteMicrophone_liveSeatStore.unmuteMicrophone();} else {debugPrint("Response startLive onError: ${result.message}");}}}
Parameter Name | Type | Attribute | Description |
liveID | String | Required | Unique identifier of the live streaming room. |
liveName | String | Optional. | Title of the live streaming room. |
notice | String | Optional. | Announcement information of the live streaming room. |
isMessageDisable | bool | Optional. | Whether to mute chat ( true: yes, false: no). |
isPublicVisible | bool | Optional. | Make room public ( true: yes, false: no) |
isSeatEnabled | bool | Optional. | Whether to enable the seat feature ( true: yes, false: no). |
keepOwnerOnSeat | bool | Optional. | Whether to keep the room owner on the seat. |
maxSeatCount | int | Optional. | Maximum number of microphones. |
seatMode | TakeSeatMode | Optional. | Microphone mode ( free: free to take seat, apply: apply to take seat). |
seatLayoutTemplateID | int | Required | Mic position layout template ID. |
coverURL | String | Optional. | Cover image URL for the live room. |
backgroundURL | String | Optional. | Background image URL of the live streaming room. |
categoryList | List<int> | Optional. | Category tag list of the live streaming room. |
activityStatus | int | Optional. | Live stream status. |
isGiftEnabled | bool | Optional. | Whether to enable the gift function ( true: yes, false: no). |
UI effect, see the seat_grid_widget.dart file in the TUILiveKit open-source project to learn about the complete implementation logic.LiveSeatStore instance, listen to changes in liveSeatState.seatList to get seat data in real time for rendering your UI. You can listen to the data on the page (such as YourAnchorPage or YourAudiencePage) in the following ways:class _YourAnchorPageState extends State<YourAnchorPage> {// ... Other code ...late final LiveSeatStore _liveSeatStore;late final VoidCallback _seatListListener = _onSeatListChanged;@overridevoid initState() {super.initState();_liveSeatStore = LiveSeatStore.create("your_live_id");// ... Other code ...// Listen to seatList transition_observeSeatList();}void _observeSeatList() {// Listen to changes in liveSeatState.seatList and refresh your mic seat UI_liveSeatStore.liveSeatState.seatList.addListener(_seatListListener);}void _onSeatListChanged() {// seatInfoList is the latest microphone position list (List<SeatInfo>)final seatInfoList = _liveSeatStore.liveSeatState.seatList.value;// Render your mic seat UI here based on seatInfoListdebugPrint("Seat list updated: ${seatInfoList.length} seats");setState(() {});}@overridevoid dispose() {_liveSeatStore.liveSeatState.seatList.removeListener(_seatListListener);super.dispose();}@overrideWidget build(BuildContext context) {return ValueListenableBuilder<List<SeatInfo>>(valueListenable: _liveSeatStore.liveSeatState.seatList,builder: (context, seatList, child) {return GridView.builder(gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4,),itemCount: seatList.length,itemBuilder: (context, index) {final seat = seatList[index];return _buildSeatItem(seat);},);},);}Widget _buildSeatItem(SeatInfo seat) {// Build one mic seat UIreturn Container();}}
LiveListStore's endLive API to end the chat. The SDK will handle the logic of stopping streaming and terminating the room.class _YourAnchorPageState extends State<YourAnchorPage> {// ... Other code ...// End Voice ChatFuture<void> _stopLive() async {final result = await _liveListStore.endLive();if (result.isSuccess) {debugPrint("endLive success");} else {debugPrint("endLive error: ${result.message}");}}}
LiveSeatStore instance and listen to changes in liveSeatState.seatList to render the mic seat UI.import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';// YourAudiencePage represents your audience pageclass YourAudiencePage extends StatefulWidget {const YourAudiencePage({super.key});@overrideState<YourAudiencePage> createState() => _YourAudiencePageState();}class _YourAudiencePageState extends State<YourAudiencePage> {final _liveListStore = LiveListStore.shared;// Ensure the liveID matches the room ownerfinal String _liveID = "test_voice_room_001";late final LiveSeatStore _liveSeatStore;late final VoidCallback _seatListListener = _onSeatListChanged;@overridevoid initState() {super.initState();_liveSeatStore = LiveSeatStore.create(_liveID);// Listen to microphone position list adjustment_observeSeatList();}void _observeSeatList() {// Listen to changes in liveSeatState.seatList and refresh your mic seat UI_liveSeatStore.liveSeatState.seatList.addListener(_seatListListener);}void _onSeatListChanged() {// Render your mic seat UI here based on seatInfoListfinal seatInfoList = _liveSeatStore.liveSeatState.seatList.value;debugPrint("AudiencePage Seat list updated: ${seatInfoList.length} seats");setState(() {});}@overridevoid dispose() {_liveSeatStore.liveSeatState.seatList.removeListener(_seatListListener);super.dispose();}@overrideWidget build(BuildContext context) {// Assume that you have your own layoutreturn Container();}}
LiveListStore joinLive API to join a voice chat room, sample code:class _YourAudiencePageState extends State<YourAudiencePage> {// ... Other code ...@overridevoid initState() {super.initState();// ... Other code ...// Enter a voice chat room_joinLive();}Future<void> _joinLive() async {// Call joinLive to enter a voice chat roomfinal result = await _liveListStore.joinLive(_liveID);if (result.isSuccess) {debugPrint("joinLive success");} else {debugPrint("joinLive error: ${result.message}");}}}
LiveListStore's leaveLive API to exit.class _YourAudiencePageState extends State<YourAudiencePage> {// ... Other code ...Future<void> _leaveLive() async {final result = await _liveListStore.leaveLive();if (result.isSuccess) {debugPrint("leaveLive success");} else {debugPrint("leaveLive error: ${result.message}");}}}

LiveSeatStore provides the speakingUsers data stream, specialized for implementing this feature.
TUILiveKit open-source project to learn about the complete implementation logic.YourAnchorPage or YourAudiencePage, listen for speakingUsers transition and refresh the "speaking" status. Sample code:class _YourAnchorPageState extends State<YourAnchorPage> {late final VoidCallback _speakingUsersListener = _onSpeakingUsersChanged;// ... Other code ...@overridevoid initState() {super.initState();// ... Other code ...// Listen to speakingUsers transition_observeSpeakingUsersState();}void _observeSpeakingUsersState() {// Listen for liveSeatState.speakingUsers transition and refresh the "speaking" status_liveSeatStore.liveSeatState.speakingUsers.addListener(_speakingUsersListener);}void _onSpeakingUsersChanged() {// Pass the user ID collection of "speaking" users to the UI and update the UI statefinal speakingUserMap = _liveSeatStore.liveSeatState.speakingUsers.value;debugPrint("Speaking users updated: ${speakingUserMap.length} users");setState(() {});}@overridevoid dispose() {_liveSeatStore.liveSeatState.speakingUsers.removeListener(_speakingUsersListener);super.dispose();}@overrideWidget build(BuildContext context) {return ValueListenableBuilder<Map<String, int>>(valueListenable: _liveSeatStore.liveSeatState.speakingUsers,builder: (context, speakingUsers, child) {// speakingUsers is a Map where the key is userID and the value is volumereturn YourSpeakingIndicatorWidget(speakingUsers: speakingUsers);},);}}// Example: Speaking indicator componentclass YourSpeakingIndicatorWidget extends StatelessWidget {final Map<String, int> speakingUsers;const YourSpeakingIndicatorWidget({super.key,required this.speakingUsers,});@overrideWidget build(BuildContext context) {return Container();}}
Feature | Feature Description | Feature Stores | Implementation Guide |
Audience Take Seat | Audience members can apply to take a seat and interact with the host in real time. | ||
Add Barrage Chat | Room members can send and receive real-time text messages. | ||
Gift System | Audience can send virtual gifts to hosts to increase interaction and engagement. |
Store/Component | Feature Description | API Reference |
LiveListStore | Full lifecycle management of live streaming room: create/join/leave/terminate room, query room list, modify live information (name, notice), listen to live streaming status (for example, being kicked out, ended). | |
LiveSeatStore | Core of seat management: manage seat list, Anchor status, and related operations (taking a seat, leaving a seat, removing user, locking seat, switching microphone on or off/camera), listen to seat events. | |
DeviceStore | Audio/Video device control: microphone (on/off, volume), camera (on/off, switchover, video quality), screen sharing, monitor device status in real time. | |
CoGuestStore | Audience co-broadcasting management: join microphone application/invite/grant/deny, member permission control (microphone/camera), state synchronization. | |
CoHostStore | Anchor cross-room connection: support for multiple layout templates (dynamic grid, etc.), initiate/accept/reject connection, and interactive management of co-broadcasting Anchors. | |
BattleStore | Anchor PK battle: initiate PK (duration configuration/competitor), manage PK status (start/end), synchronize score, listen to battle results. | |
GiftStore | Gift interaction: get gift list, send/receive gifts, listen to gift events (including sender, gift details). | |
BarrageStore | Danmaku function: send text/custom barrage item, maintain bullet screen list, monitor danmaku status in real time. | |
LikeStore | Like interaction: send likes, listen to like events, sync total likes. | |
LiveAudienceStore | Audience management: get real-time audience list (ID/name/avatar), check audience size, listen to Enter/Exit Event. | |
AudioEffectStore | Audio effects: voice-changing (child voice/male voice), reverberation (KTV), ear monitoring adjustment, switch special effects in real time. | |
BaseBeautyStore | Basic beauty filter: adjust skin smoothing/whitening/rosy (0-100 levels), reset beauty status, sync effect parameters. |
App has declared and obtained system authorization for microphone in Info.plist (iOS) or AndroidManifest.xml (Android).DeviceStore.shared.openLocalMicrophone() to open the microphone.liveID to correctly create the LiveSeatStore instance (LiveSeatStore.create(liveID)) before createLive or joinLive.addListener correctly to listen to liveSeatStore.liveSeatState.seatList, and ensure to call removeListener in dispose to remove listener.createLive (room owner) or joinLive (listener) API has been successfully called (confirm when isSuccess in the callback result is true).Feedback