tencent cloud

Quick Start (Flutter)
Last updated:2026-02-27 20:58:06
Quick Start (Flutter)
Last updated: 2026-02-27 20:58:06
This guides you through building a voice chat room application with host broadcasting and audience participation features using the AtomicXCore SDK's LiveListStore and LiveSeatStore.

Core Concepts

The following table introduces core concepts of 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.
SeatInfo
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).

Preparations

Step 1: Activate the Services

See Activate Service to obtain either the trial or paid version of the SDK.Then, go to the Console for application management, and get the following:
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.


Step 2: Import AtomicXCore into Your Project

1. Install component: Please add atomic_x_core dependency in your pubspec.yaml file, then execute flutter pub get.
dependencies:
atomic_x_core: ^3.6.0
2. Configure project permissions: Android and iOS projects all are needed to configure permissions.
Android
iOS
Add permission to use microphone in file android/app/src/main/AndroidManifest.xml.
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Add permission to use microphone in file ios directory Podfile and ios/Runner directory Info.plist.
Podfile
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',
]
end
end
end
Info.plist
<key>NSMicrophoneUsageDescription</key>
<string>TUILiveKit needs to access your microphone permission. Recorded video will have sound when enabled</string>


Step 3: Implement Login Logic

Call LoginStore.shared.login in the project to complete login. This is the key premise for using AtomicXCore all functions.
Important:
We recommend calling LoginStore.shared.login after successful log-in to your App's user account to ensure clear and consistent login service logic.
import 'package:flutter/material.dart';
import 'package:atomic_x_core/atomicxcore.dart';

// main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();

// Log in.
final result = await LoginStore.shared.login(
sdkAppID: 1400000001, // Replace with your sdkAppID
userID: "test_001", // Replace with your user ID
userSig: "xxxxxxxxxxx", // Replace with your userSig
);

if (result.isSuccess) {
debugPrint("login success");
} else {
debugPrint("login failed code: ${result.code}, message: ${result.message}");
}

runApp(const MyApp());
}
Login API parameters:
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.

Build a Basic Voice Chat Room

Step 1: Host Creates a Voice Chat Room

Follow these steps to quickly set up a voice chat room as the host.

1. Initialize LiveSeatStore

On your anchor page, create a 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 homepage
class YourAnchorPage extends StatefulWidget {
const YourAnchorPage({super.key});

@override
State<YourAnchorPage> createState() => _YourAnchorPageState();
}

class _YourAnchorPageState extends State<YourAnchorPage> {
final _liveListStore = LiveListStore.shared;
final _deviceStore = DeviceStore.shared;

// Initialize LiveSeatStore with liveID
final String _liveID = "test_voice_room_001";
late final LiveSeatStore _liveSeatStore;
late final VoidCallback _seatListListener = _onSeatListChanged;

@override
void 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 seatInfoList
final seatInfoList = _liveSeatStore.liveSeatState.seatList.value;
debugPrint("Seat list updated: ${seatInfoList.length} seats");
setState(() {});
}

@override
void dispose() {
_liveSeatStore.liveSeatState.seatList.removeListener(_seatListListener);
super.dispose();
}

@override
Widget build(BuildContext context) {
// Assume that you have your own layout
return Container();
}
}

2. Turn on Microphone

Turn on the microphone by calling the DeviceStore openLocalMicrophone API:
class _YourAnchorPageState extends State<YourAnchorPage> {
// ... Other code ...

void _openDevices() {
// Turn the mic on
DeviceStore.shared.openLocalMicrophone();
}
}

3. Start Voice Chat

By calling the 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";

@override
void initState() {
super.initState();
// ... Other code ...

// Start Voice Chat
_startLive();
}

Future<void> _startLive() async {
// 1. Prepare the LiveInfo object
final liveInfo = LiveInfo(
// 2. Set the room id
liveID: _liveID,
// 3. Set the room name
liveName: "test voice chat room",
// 4. Configured as a voice chat room (enable seat)
isSeatEnabled: true,
// 5. Room owner on seat by default
keepOwnerOnSeat: true,
// 6. Set the seat layout template (for example, 70 is the 10-seat template)
// Important: According to the product specification, input the correct ID
seatLayoutTemplateID: 70,
// 7. Set the microphone mode, such as apply for microphone mode
seatMode: TakeSeatMode.apply,
// 8. Set the maximum number of microphone slots
maxSeatCount: 10,
);

// 9. Call createLive to start live streaming
final 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}");
}
}
}
LiveInfo parameters:
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).

4. Build a Seat UI

Note:
For the business code of the Mic Seat UI effect, see the seat_grid_widget.dart file in the TUILiveKit open-source project to learn about the complete implementation logic.
Through the 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;

@override
void 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 seatInfoList
debugPrint("Seat list updated: ${seatInfoList.length} seats");
setState(() {});
}

@override
void dispose() {
_liveSeatStore.liveSeatState.seatList.removeListener(_seatListListener);
super.dispose();
}

@override
Widget 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 UI
return Container();
}
}

5. End Voice Chat

After the voice chat, the room owner can call the 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 Chat
Future<void> _stopLive() async {
final result = await _liveListStore.endLive();

if (result.isSuccess) {
debugPrint("endLive success");
} else {
debugPrint("endLive error: ${result.message}");
}
}
}

Step 2: Audience Joins the Voice Chat Room

Enable audience members to join the voice chat room with the following steps.

1. Initialize the LiveSeatStore

On your audience page, create a 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 page
class YourAudiencePage extends StatefulWidget {
const YourAudiencePage({super.key});

@override
State<YourAudiencePage> createState() => _YourAudiencePageState();
}

class _YourAudiencePageState extends State<YourAudiencePage> {
final _liveListStore = LiveListStore.shared;

// Ensure the liveID matches the room owner
final String _liveID = "test_voice_room_001";
late final LiveSeatStore _liveSeatStore;
late final VoidCallback _seatListListener = _onSeatListChanged;

@override
void 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 seatInfoList
final seatInfoList = _liveSeatStore.liveSeatState.seatList.value;
debugPrint("AudiencePage Seat list updated: ${seatInfoList.length} seats");
setState(() {});
}

@override
void dispose() {
_liveSeatStore.liveSeatState.seatList.removeListener(_seatListListener);
super.dispose();
}

@override
Widget build(BuildContext context) {
// Assume that you have your own layout
return Container();
}
}

2. Join the Voice Chat Room

By calling the LiveListStore joinLive API to join a voice chat room, sample code:
class _YourAudiencePageState extends State<YourAudiencePage> {
// ... Other code ...

@override
void initState() {
super.initState();
// ... Other code ...

// Enter a voice chat room
_joinLive();
}

Future<void> _joinLive() async {
// Call joinLive to enter a voice chat room
final result = await _liveListStore.joinLive(_liveID);

if (result.isSuccess) {
debugPrint("joinLive success");
} else {
debugPrint("joinLive error: ${result.message}");
}
}
}

3. Build a Seat UI

Audience seat UI implementation is identical to the host. Refer to Build a Mic Seat UI.

4. Leave the Voice Chat Room

When the audience logs out of the voice chat room, they need to call the 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}");
}
}
}

Run and Test

After completing the above steps, you can complete a most basic voice chat live streaming scenario. You can refer to various voice chat room scenarios to refine the voice chat scenario.


Advanced Features

Display Speaking Wave Animation for Users on Seat

In a voice chat room scenario, a common need is to display a wave animation on the avatar of an on-mic user when they speak, notifying all room users "who is speaking". LiveSeatStore provides the speakingUsers data stream, specialized for implementing this feature.


Implementation

Note:
When an Anchor speaks, a wave animation effect is shown. See the seat_grid_widget.dart file in the TUILiveKit open-source project to learn about the complete implementation logic.
On 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 ...

@override
void 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 state
final speakingUserMap = _liveSeatStore.liveSeatState.speakingUsers.value;
debugPrint("Speaking users updated: ${speakingUserMap.length} users");
setState(() {});
}

@override
void dispose() {
_liveSeatStore.liveSeatState.speakingUsers.removeListener(_speakingUsersListener);
super.dispose();
}

@override
Widget 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 volume
return YourSpeakingIndicatorWidget(speakingUsers: speakingUsers);
},
);
}
}

// Example: Speaking indicator component
class YourSpeakingIndicatorWidget extends StatelessWidget {
final Map<String, int> speakingUsers;

const YourSpeakingIndicatorWidget({
super.key,
required this.speakingUsers,
});

@override
Widget build(BuildContext context) {
return Container();
}
}

Enrich Voice Chat Room Scenarios

After completing the basic voice chat room features, you can refer to the following feature guide to add various interactive gameplay features to the voice chat room.
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.
GiftStore

API documentation

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.

FAQs

No Sound After Audience Joins Room

Check device permissions: Please ensure App has declared and obtained system authorization for microphone in Info.plist (iOS) or AndroidManifest.xml (Android).
Check the homeowner side: Whether the room owner has normally called DeviceStore.shared.openLocalMicrophone() to open the microphone.
Check the network: Please check whether the device network connection is normal.

Seat List Not Displayed or Updated

Check Store initialization: Please ensure you have already used the same liveID to correctly create the LiveSeatStore instance (LiveSeatStore.create(liveID)) before createLive or joinLive.
Check data listening: Please check whether you have used addListener correctly to listen to liveSeatStore.liveSeatState.seatList, and ensure to call removeListener in dispose to remove listener.
Check API call: Please ensure the createLive (room owner) or joinLive (listener) API has been successfully called (confirm when isSuccess in the callback result is true).
Was this page helpful?
You can also Contact Sales or Submit a Ticket for help.
Yes
No

Feedback