@tencent-rtc/mcp 1.3.5 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/index.js +4 -4
  2. package/index.js.map +1 -1
  3. package/package.json +1 -1
  4. package/resource/doc/livekit/live/flutter/live-audience-list.md +339 -0
  5. package/resource/doc/livekit/live/flutter/live-audio-effect.md +742 -0
  6. package/resource/doc/livekit/live/flutter/live-barrage.md +581 -0
  7. package/resource/doc/livekit/live/flutter/live-base-beauty.md +486 -0
  8. package/resource/doc/livekit/live/flutter/live-co-guest-interaction.md +608 -0
  9. package/resource/doc/livekit/live/flutter/live-gift.md +800 -0
  10. package/resource/doc/livekit/live/flutter/live-host-interaction.md +1113 -0
  11. package/resource/doc/livekit/live/flutter/live-integration.md +1041 -0
  12. package/resource/doc/livekit/live/flutter/live-live-list.md +865 -0
  13. package/resource/doc/livekit/live/flutter/workflow.md +353 -0
  14. package/resource/doc/tuicallkit/flutter/workflow.md +331 -0
  15. package/tools/call/get-native-call-integration.js +27 -11
  16. package/tools/call/get-native-call-integration.js.map +1 -1
  17. package/tools/definitions/get_native_call_uikit_integration_def.js +17 -7
  18. package/tools/definitions/get_native_call_uikit_integration_def.js.map +1 -1
  19. package/utils/report-cls-client.js +1 -1
  20. package/utils/report-es-client.js +1 -1
  21. package/resource/doc/tuicallkit/flutter/how_to_run_flutter_callkit_demo.md +0 -101
  22. package/resource/doc/tuicallkit/iOS/how_to_run_iOS_callkit_demo.md +0 -98
  23. /package/resource/doc/tuicallkit/android/{how_to_integrated_android_callkit.md → integration.md} +0 -0
  24. /package/resource/doc/tuicallkit/android/{.gitkeep → workflow.md} +0 -0
  25. /package/resource/doc/tuicallkit/{android/api_TUICallObserver.md → faq/android_sdk/call-observer-api.md} +0 -0
  26. /package/resource/doc/tuicallkit/{android/api_TUICallKit.md → faq/android_sdk/callkit-api.md} +0 -0
  27. /package/resource/doc/tuicallkit/{flutter/api_TUICallDefine.md → faq/flutter_sdk/call-define-api.md} +0 -0
  28. /package/resource/doc/tuicallkit/{flutter/api_TUICallObserver.md → faq/flutter_sdk/call-observer-api.md} +0 -0
  29. /package/resource/doc/tuicallkit/{flutter/api_TUICallKit.md → faq/flutter_sdk/callkit-api.md} +0 -0
  30. /package/resource/doc/tuicallkit/{iOS/api_TUICallObserver.md → faq/ios_sdk/call-observer-api.md} +0 -0
  31. /package/resource/doc/tuicallkit/{iOS/api_TUICallKit.md → faq/ios_sdk/callkit-api.md} +0 -0
  32. /package/resource/doc/tuicallkit/flutter/{how_to_integrated_flutter_callkit.md → integration.md} +0 -0
  33. /package/resource/doc/tuicallkit/iOS/{how_to_integrated_iOS_callkit.md → integration.md} +0 -0
@@ -0,0 +1,1041 @@
1
+ This documentation guides you through using the core component of the AtomicXCore SDK, [LiveCoreWidget](https://tencent-rtc.github.io/TUIKit_Flutter/api_view_live_live_core_widget/api_view_live_live_core_widget-library.html), to quickly build a basic live streaming app with host broadcasting and audience viewing functionality.
2
+
3
+ ## Core Features
4
+
5
+ **LiveCoreWidget** is a lightweight widget purpose-built for live streaming scenarios and serves as the foundation of your live streaming interface. It abstracts complex live streaming technologies—including streaming, co-hosting, and audio/video rendering—so you can use LiveCoreWidget as the canvas for your live video. This allows you to focus on developing your custom UI and interactive features.
6
+
7
+ The following diagram illustrates where **LiveCoreWidget** fits within your live streaming interface and its role in the view hierarchy:
8
+
9
+ ![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100032457995/989ce871f6d411f09fbf525400370dda.png)
10
+
11
+
12
+ ## Preparations
13
+
14
+ ### Step 1: Activate the Service
15
+
16
+ See [Activate Service](https://write.woa.com/document/141936672617742336) to obtain either the trial or paid version of the SDK.Then, go to [the Console](https://console.trtc.io/app) for application management, and get the following:
17
+ - `SDKAppID`: Application identifier (required). Tencent Cloud uses `SDKAppID` for billing and details.
18
+
19
+ - `SDKSecretKey`: Application secret key, used to initialize the configuration file with secret information.
20
+
21
+ ![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100032457995/bb11696df6d411f09d0352540097cba1.png)
22
+
23
+
24
+ ### Step 2: Import AtomicXCore into Your Project
25
+ 1. **Component installation**: Please add the `atomic_x_core` dependency in your `pubspec.yaml` file and execute `flutter pub get`.
26
+
27
+ ``` yaml
28
+ dependencies:
29
+ atomic_x_core: ^3.6.0
30
+ ```
31
+ 2. **Configure project permissions:** Both Android and iOS projects require configuration.
32
+
33
+
34
+
35
+
36
+ 【Android】
37
+
38
+ Please add permission to use camera and microphone in the `android/app/src/main/AndroidManifest.xml` file.
39
+ ``` xml
40
+ <uses-permission android:name="android.permission.CAMERA" />
41
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
42
+ ```
43
+
44
+ 【iOS】
45
+
46
+ Please add permission to use camera and microphone in the `iOS` directory **Podfile** and `ios/Runner` directory `Info.plist` file.
47
+
48
+ **Podfile**
49
+ ``` ruby
50
+ post_install do |installer|
51
+ installer.pods_project.targets.each do |target|
52
+ flutter_additional_ios_build_settings(target)
53
+ target.build_configurations.each do |config|
54
+ config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
55
+ '$(inherited)',
56
+ 'PERMISSION_MICROPHONE=1',
57
+ 'PERMISSION_CAMERA=1',
58
+ ]
59
+ end
60
+ end
61
+ end
62
+ ```
63
+
64
+ **Info.plist**
65
+ ``` xml
66
+ <key>NSCameraUsageDescription</key>
67
+ <string>TUILiveKit needs to access your camera permission. Video recording with picture only after enabling.</string>
68
+ <key>NSMicrophoneUsageDescription</key>
69
+ <string>TUILiveKit needs to access your mic permission. Recorded video will have sound when enabled.</string>
70
+ ```
71
+
72
+ ![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100032457995/548e6c77f82711f093ec525400074c32.png)
73
+
74
+
75
+
76
+ ### Step 3: Implement Login Logic
77
+
78
+ Call `LoginStore.shared.login` in your project to complete login. **This is the key premise for using all functions of AtomicXCore.**
79
+
80
+ > **Important:**
81
+ >
82
+
83
+ > It is recommended to call LoginStore.shared.login after successful log-in to your App's user account to ensure clear and consistent business logic.
84
+ >
85
+
86
+ ``` java
87
+ import 'package:flutter/material.dart';
88
+ import 'package:atomic_x_core/atomicxcore.dart';
89
+
90
+ void main() async {
91
+ WidgetsFlutterBinding.ensureInitialized();
92
+
93
+ final result = await LoginStore.shared.login(
94
+ sdkAppID: 1400000001, // replace with your sdkAppID
95
+ userID: "test_001", // replace with your userID
96
+ userSig: "xxxxxxxxxxx", // replace with your userSig
97
+ );
98
+
99
+ if (result.isSuccess) {
100
+ debugPrint("login success");
101
+ } else {
102
+ debugPrint("login failed code: ${result.errorCode}, message: ${result.errorMessage}");
103
+ }
104
+
105
+ runApp(const MyApp());
106
+ }
107
+ ```
108
+
109
+ **Logon API parameter description:**
110
+ <table>
111
+ <tr>
112
+ <td rowspan="1" colSpan="1" >**Parameter**</td>
113
+
114
+ <td rowspan="1" colSpan="1" >**Type**</td>
115
+
116
+ <td rowspan="1" colSpan="1" >**Note**</td>
117
+ </tr>
118
+
119
+ <tr>
120
+ <td rowspan="1" colSpan="1" >`sdkAppID`</td>
121
+
122
+ <td rowspan="1" colSpan="1" >`int`</td>
123
+
124
+ <td rowspan="1" colSpan="1" >Get this from [TRTC console](https://console.trtc.io/app).</td>
125
+ </tr>
126
+
127
+ <tr>
128
+ <td rowspan="1" colSpan="1" >`userID`</td>
129
+
130
+ <td rowspan="1" colSpan="1" >`String`</td>
131
+
132
+ <td rowspan="1" colSpan="1" >The unique ID for the current user. Must contain only English letters, numbers, hyphens, and underscores.. To avoid multi-end login conflict, **do not use**`1`, `123`**or other simple IDs**.</td>
133
+ </tr>
134
+
135
+ <tr>
136
+ <td rowspan="1" colSpan="1" >`userSig`</td>
137
+
138
+ <td rowspan="1" colSpan="1" >`String`</td>
139
+
140
+ <td rowspan="1" colSpan="1" >A ticket for Tencent Cloud authentication. Please note:<br>- Development Environment: You can use the local `GenerateTestUserSig.genTestSig` function to generate a `UserSig` or generate a temporary UserSig via the [UserSig Generation Tool](https://console.trtc.io/usersig).<br>- Production Environment: To prevent key leakage, you must use a server-side method to generate `UserSig`. For details, see [Generating UserSig on the Server](https://write.woa.com/document/89644530319802368).</td>
141
+ </tr>
142
+ </table>
143
+
144
+
145
+ ## Building a Basic Live Room
146
+
147
+ ### Step 1: Implement Broadcaster Video Streaming
148
+
149
+ The anchor starts live streaming process is as follows. You just need to perform the following steps to quickly build a live stream.
150
+
151
+ ![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100032457995/332cea8bf6d711f09fbf525400370dda.png)
152
+
153
+
154
+ > **Note:**
155
+ >
156
+
157
+ > For the business code of live stream publishing, you can also refer to the [live_room_anchor_widget.dart](https://github.com/Tencent-RTC/TUIKit_Flutter/blob/main/live/livekit/lib/live_stream/features/live_room_anchor_widget.dart) file in the TUILiveKit open-source project to understand the complete implementation logic.
158
+ >
159
+
160
+ 1. **Initialize the Anchor live streaming view**
161
+
162
+
163
+ On your Anchor page, create a `LiveCoreWidget` instance and control the live stream behavior through `LiveCoreController`.
164
+
165
+ ``` java
166
+ import 'package:flutter/material.dart';
167
+ import 'package:atomic_x_core/atomicxcore.dart';
168
+
169
+ /// YourAnchorPage represents your anchor starts live streaming page
170
+ class YourAnchorPage extends StatefulWidget {
171
+ final String liveId;
172
+
173
+ const YourAnchorPage({super.key, required this.liveId});
174
+
175
+ @override
176
+ State<YourAnchorPage> createState() => _YourAnchorPageState();
177
+ }
178
+
179
+ class _YourAnchorPageState extends State<YourAnchorPage> {
180
+ // 1. Create a LiveCoreController instance
181
+ late final LiveCoreController _controller;
182
+
183
+ @override
184
+ void initState() {
185
+ super.initState();
186
+ // 2. Initialize the controller
187
+ _controller = LiveCoreController.create();
188
+ // 3. Set up live streaming ID
189
+ _controller.setLiveID(widget.liveId);
190
+ }
191
+
192
+ @override
193
+ Widget build(BuildContext context) {
194
+ return Scaffold(
195
+ body: Stack(
196
+ children: [
197
+ // 4. Load the broadcaster stream page to your view
198
+ LiveCoreWidget(controller: _controller),
199
+ // Your other UI components
200
+ ],
201
+ ),
202
+ );
203
+ }
204
+ }
205
+ ```
206
+ 2. **Turn on the camera and microphone**
207
+
208
+
209
+ By calling the **DeviceStore**`openLocalCamera` and `openLocalMicrophone` APIs to turn on the camera and microphone, **no need to do additional configuration as LiveCoreWidget will auto preview the current camera's video stream**. Sample code:
210
+
211
+ ``` java
212
+ import 'package:flutter/material.dart';
213
+ import 'package:atomic_x_core/atomicxcore.dart';
214
+
215
+ /// YourAnchorPage represents your anchor starts live streaming page
216
+ class YourAnchorPage extends StatefulWidget {
217
+ final String liveId;
218
+
219
+ const YourAnchorPage({super.key, required this.liveId});
220
+
221
+ @override
222
+ State<YourAnchorPage> createState() => _YourAnchorPageState();
223
+ }
224
+
225
+ class _YourAnchorPageState extends State<YourAnchorPage> {
226
+ late final LiveCoreController _controller;
227
+
228
+ @override
229
+ void initState() {
230
+ super.initState();
231
+ _controller = LiveCoreController.create();
232
+ _controller.setLiveID(widget.liveId);
233
+ // Turn on the device
234
+ _openDevices();
235
+ }
236
+
237
+ void _openDevices() {
238
+ // 1. Open front camera
239
+ DeviceStore.shared.openLocalCamera(true);
240
+ // 2. Turn on the microphone
241
+ DeviceStore.shared.openLocalMicrophone();
242
+ }
243
+
244
+ @override
245
+ Widget build(BuildContext context) {
246
+ return Scaffold(
247
+ body: Stack(
248
+ children: [
249
+ LiveCoreWidget(controller: _controller),
250
+ ],
251
+ ),
252
+ );
253
+ }
254
+ }
255
+ ```
256
+ 3. **Start live streaming**
257
+
258
+
259
+ By calling the **LiveListStore**`createLive` API to start video live streaming, sample code:
260
+
261
+ ``` java
262
+ import 'package:flutter/material.dart';
263
+ import 'package:atomic_x_core/atomicxcore.dart';
264
+
265
+ /// YourAnchorPage represents your anchor starts live streaming page
266
+ class YourAnchorPage extends StatefulWidget {
267
+ final String liveId;
268
+
269
+ const YourAnchorPage({super.key, required this.liveId});
270
+
271
+ @override
272
+ State<YourAnchorPage> createState() => _YourAnchorPageState();
273
+ }
274
+
275
+ class _YourAnchorPageState extends State<YourAnchorPage> {
276
+ late final LiveCoreController _controller;
277
+
278
+ @override
279
+ void initState() {
280
+ super.initState();
281
+ _controller = LiveCoreController.create();
282
+ _controller.setLiveID(widget.liveId);
283
+ _openDevices();
284
+ }
285
+
286
+ void _openDevices() {
287
+ DeviceStore.shared.openLocalCamera(true);
288
+ DeviceStore.shared.openLocalMicrophone();
289
+ }
290
+
291
+ // Call the go live API to start live streaming
292
+ Future<void> _startLive() async {
293
+ // 1. Prepare the LiveInfo object
294
+ final liveInfo = LiveInfo(
295
+ // Set up live streaming room id
296
+ liveID: widget.liveId,
297
+ // Set up live streaming room name
298
+ liveName: "test live stream",
299
+ // Configure the layout template, default: 600 dynamic grid layout
300
+ seatLayoutTemplateID: 600,
301
+ // Configure the anchor to always stay on the seat
302
+ keepOwnerOnSeat: true,
303
+ );
304
+
305
+ // 2. Call LiveListStore.shared.createLive to start live streaming
306
+ final result = await LiveListStore.shared.createLive(liveInfo);
307
+
308
+ if (result.isSuccess) {
309
+ debugPrint("startLive success");
310
+ } else {
311
+ debugPrint("startLive error: ${result.errorMessage}");
312
+ }
313
+ }
314
+
315
+ @override
316
+ Widget build(BuildContext context) {
317
+ return Scaffold(
318
+ body: Stack(
319
+ children: [
320
+ LiveCoreWidget(controller: _controller),
321
+ // Go live button
322
+ Positioned(
323
+ bottom: 50,
324
+ left: 0,
325
+ right: 0,
326
+ child: Center(
327
+ child: ElevatedButton(
328
+ onPressed: _startLive,
329
+ child: const Text('Start live broadcast'),
330
+ ),
331
+ ),
332
+ ),
333
+ ],
334
+ ),
335
+ );
336
+ }
337
+ }
338
+ ```
339
+
340
+ **LiveInfo parameter description:**
341
+
342
+ <table>
343
+ <tr>
344
+ <td rowspan="1" colSpan="1" >**Parameter Name**</td>
345
+
346
+ <td rowspan="1" colSpan="1" >**Type**</td>
347
+
348
+ <td rowspan="1" colSpan="1" >**Attribute**</td>
349
+
350
+ <td rowspan="1" colSpan="1" >**Description**</td>
351
+ </tr>
352
+
353
+ <tr>
354
+ <td rowspan="1" colSpan="1" >`liveID`</td>
355
+
356
+ <td rowspan="1" colSpan="1" >`String`</td>
357
+
358
+ <td rowspan="1" colSpan="1" >Required</td>
359
+
360
+ <td rowspan="1" colSpan="1" >Unique identifier for the live streaming room.</td>
361
+ </tr>
362
+
363
+ <tr>
364
+ <td rowspan="1" colSpan="1" >`liveName`</td>
365
+
366
+ <td rowspan="1" colSpan="1" >`String`</td>
367
+
368
+ <td rowspan="1" colSpan="1" >Optional.</td>
369
+
370
+ <td rowspan="1" colSpan="1" >Title of the live streaming room.</td>
371
+ </tr>
372
+
373
+ <tr>
374
+ <td rowspan="1" colSpan="1" >`notice`</td>
375
+
376
+ <td rowspan="1" colSpan="1" >`String`</td>
377
+
378
+ <td rowspan="1" colSpan="1" >Optional.</td>
379
+
380
+ <td rowspan="1" colSpan="1" >Announcement information of the live streaming room.</td>
381
+ </tr>
382
+
383
+ <tr>
384
+ <td rowspan="1" colSpan="1" >`isMessageDisable`</td>
385
+
386
+ <td rowspan="1" colSpan="1" >`bool`</td>
387
+
388
+ <td rowspan="1" colSpan="1" >Optional.</td>
389
+
390
+ <td rowspan="1" colSpan="1" >Whether to mute (`true`: yes, `false`: no).</td>
391
+ </tr>
392
+
393
+ <tr>
394
+ <td rowspan="1" colSpan="1" >`isPublicVisible`</td>
395
+
396
+ <td rowspan="1" colSpan="1" >`bool`</td>
397
+
398
+ <td rowspan="1" colSpan="1" >Optional.</td>
399
+
400
+ <td rowspan="1" colSpan="1" >Whether publicly visible (`true`: yes, `false`: no).</td>
401
+ </tr>
402
+
403
+ <tr>
404
+ <td rowspan="1" colSpan="1" >`isSeatEnabled`</td>
405
+
406
+ <td rowspan="1" colSpan="1" >`bool`</td>
407
+
408
+ <td rowspan="1" colSpan="1" >Optional.</td>
409
+
410
+ <td rowspan="1" colSpan="1" >Whether to enable the seat feature (`true`: yes, `false`: no).</td>
411
+ </tr>
412
+
413
+ <tr>
414
+ <td rowspan="1" colSpan="1" >`keepOwnerOnSeat`</td>
415
+
416
+ <td rowspan="1" colSpan="1" >`bool`</td>
417
+
418
+ <td rowspan="1" colSpan="1" >Optional.</td>
419
+
420
+ <td rowspan="1" colSpan="1" >Whether to keep the room owner on the seat.</td>
421
+ </tr>
422
+
423
+ <tr>
424
+ <td rowspan="1" colSpan="1" >`maxSeatCount`</td>
425
+
426
+ <td rowspan="1" colSpan="1" >`int`</td>
427
+
428
+ <td rowspan="1" colSpan="1" >Optional.</td>
429
+
430
+ <td rowspan="1" colSpan="1" >Maximum number of microphones.</td>
431
+ </tr>
432
+
433
+ <tr>
434
+ <td rowspan="1" colSpan="1" >`seatMode`</td>
435
+
436
+ <td rowspan="1" colSpan="1" >`TakeSeatMode`</td>
437
+
438
+ <td rowspan="1" colSpan="1" >Optional.</td>
439
+
440
+ <td rowspan="1" colSpan="1" >Microphone mode (`TakeSeatMode.free`: Free to Join the Podium, `TakeSeatMode.apply`: Request to speak).</td>
441
+ </tr>
442
+
443
+ <tr>
444
+ <td rowspan="1" colSpan="1" >`seatLayoutTemplateID`</td>
445
+
446
+ <td rowspan="1" colSpan="1" >`int`</td>
447
+
448
+ <td rowspan="1" colSpan="1" >Required</td>
449
+
450
+ <td rowspan="1" colSpan="1" >layout template ID.</td>
451
+ </tr>
452
+
453
+ <tr>
454
+ <td rowspan="1" colSpan="1" >`coverURL`</td>
455
+
456
+ <td rowspan="1" colSpan="1" >`String`</td>
457
+
458
+ <td rowspan="1" colSpan="1" >Optional.</td>
459
+
460
+ <td rowspan="1" colSpan="1" >Thumbnail URL of the live streaming room.</td>
461
+ </tr>
462
+
463
+ <tr>
464
+ <td rowspan="1" colSpan="1" >`backgroundURL`</td>
465
+
466
+ <td rowspan="1" colSpan="1" >`String`</td>
467
+
468
+ <td rowspan="1" colSpan="1" >Optional.</td>
469
+
470
+ <td rowspan="1" colSpan="1" >Background image URL of the live streaming room.</td>
471
+ </tr>
472
+
473
+ <tr>
474
+ <td rowspan="1" colSpan="1" >`categoryList`</td>
475
+
476
+ <td rowspan="1" colSpan="1" >`List<int>`</td>
477
+
478
+ <td rowspan="1" colSpan="1" >Optional.</td>
479
+
480
+ <td rowspan="1" colSpan="1" >Tag list of categories in the live streaming room.</td>
481
+ </tr>
482
+
483
+ <tr>
484
+ <td rowspan="1" colSpan="1" >`activityStatus`</td>
485
+
486
+ <td rowspan="1" colSpan="1" >`int`</td>
487
+
488
+ <td rowspan="1" colSpan="1" >Optional.</td>
489
+
490
+ <td rowspan="1" colSpan="1" >Live stream status.</td>
491
+ </tr>
492
+
493
+ <tr>
494
+ <td rowspan="1" colSpan="1" >`isGiftEnabled`</td>
495
+
496
+ <td rowspan="1" colSpan="1" >`bool`</td>
497
+
498
+ <td rowspan="1" colSpan="1" >Optional.</td>
499
+
500
+ <td rowspan="1" colSpan="1" >Whether to enable the gift function (`true`: yes, `false`: no).</td>
501
+ </tr>
502
+ </table>
503
+
504
+ 4. **End live streaming**
505
+
506
+
507
+ After the live streaming ends, the Anchor can call the **LiveListStore**'s `endLive` API to end the live stream. The SDK will handle the logic of stopping streaming and room termination.
508
+
509
+ ``` java
510
+ import 'package:flutter/material.dart';
511
+ import 'package:atomic_x_core/atomicxcore.dart';
512
+
513
+ /// YourAnchorPage represents your anchor starts live streaming page
514
+ class YourAnchorPage extends StatefulWidget {
515
+ final String liveId;
516
+
517
+ const YourAnchorPage({super.key, required this.liveId});
518
+
519
+ @override
520
+ State<YourAnchorPage> createState() => _YourAnchorPageState();
521
+ }
522
+
523
+ class _YourAnchorPageState extends State<YourAnchorPage> {
524
+ // ... Other code ...
525
+
526
+ End live streaming
527
+ Future<void> _stopLive() async {
528
+ final result = await LiveListStore.shared.endLive();
529
+
530
+ if (result.isSuccess) {
531
+ debugPrint("endLive success");
532
+ } else {
533
+ debugPrint("endLive error: ${result.errorMessage}");
534
+ }
535
+ }
536
+
537
+ // ... Other code ...
538
+ }
539
+ ```
540
+
541
+ ### Step 2: Implement Audience Join and Watch
542
+
543
+ The audience viewing process is as follows. In just a few simple steps, you can achieve audience watching live.
544
+
545
+ ![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100032457995/92e9b016f6d711f0b34b525400ecee81.png)
546
+
547
+
548
+ > **Note:**
549
+ >
550
+
551
+ > For the business code of audience enter room to pull stream, you can also refer to the [audience_widget.dart](https://github.com/Tencent-RTC/TUIKit_Flutter/blob/main/live/livekit/lib/live_stream/features/audience/audience_widget.dart) file in the TUILiveKit open-source project to understand the complete implementation logic.
552
+ >
553
+
554
+ 1. **Implement the audience pull stream page**
555
+
556
+
557
+ On your audience page, create a `LiveCoreWidget` instance and control the live stream behavior through `LiveCoreController`.
558
+
559
+ ``` java
560
+ import 'package:flutter/material.dart';
561
+ import 'package:atomic_x_core/atomicxcore.dart';
562
+
563
+ /// YourAudiencePage represents your audience viewing webpage
564
+ class YourAudiencePage extends StatefulWidget {
565
+ final String liveId;
566
+
567
+ const YourAudiencePage({super.key, required this.liveId});
568
+
569
+ @override
570
+ State<YourAudiencePage> createState() => _YourAudiencePageState();
571
+ }
572
+
573
+ class _YourAudiencePageState extends State<YourAudiencePage> {
574
+ // 1. Create a LiveCoreController instance
575
+ late final LiveCoreController _controller;
576
+
577
+ @override
578
+ void initState() {
579
+ super.initState();
580
+ // 2. Initialize the controller
581
+ _controller = LiveCoreController.create();
582
+ // 3. Bind live streaming id
583
+ _controller.setLiveID(widget.liveId);
584
+ }
585
+
586
+ @override
587
+ Widget build(BuildContext context) {
588
+ return Scaffold(
589
+ body: Stack(
590
+ children: [
591
+ // 4. Load the audience pull stream page to your view
592
+ LiveCoreWidget(controller: _controller),
593
+ ],
594
+ ),
595
+ );
596
+ }
597
+ }
598
+ ```
599
+ 2. **Enter live room to watch**
600
+
601
+
602
+ By calling the **LiveListStore**`joinLive` API to join the live stream, **no need to do additional configuration, LiveCoreWidget will autoplay the current room video stream**, sample code:
603
+
604
+ ``` java
605
+ import 'package:flutter/material.dart';
606
+ import 'package:atomic_x_core/atomicxcore.dart';
607
+
608
+ /// YourAudiencePage represents your audience viewing webpage
609
+ class YourAudiencePage extends StatefulWidget {
610
+ final String liveId;
611
+
612
+ const YourAudiencePage({super.key, required this.liveId});
613
+
614
+ @override
615
+ State<YourAudiencePage> createState() => _YourAudiencePageState();
616
+ }
617
+
618
+ class _YourAudiencePageState extends State<YourAudiencePage> {
619
+ late final LiveCoreController _controller;
620
+
621
+ @override
622
+ void initState() {
623
+ super.initState();
624
+ _controller = LiveCoreController.create();
625
+ _controller.setLiveID(widget.liveId);
626
+ // Enter live room
627
+ _joinLive();
628
+ }
629
+
630
+ Future<void> _joinLive() async {
631
+ // Call LiveListStore.shared.joinLive to enter live room
632
+ // - liveId: same liveId as anchor starts live streaming
633
+ final result = await LiveListStore.shared.joinLive(widget.liveId);
634
+
635
+ if (result.isSuccess) {
636
+ debugPrint("joinLive success");
637
+ } else {
638
+ debugPrint("joinLive error: ${result.errorMessage}");
639
+ // Failed to enter the room, must exit the page
640
+ // if (mounted) Navigator.of(context).pop();
641
+ }
642
+ }
643
+
644
+ @override
645
+ Widget build(BuildContext context) {
646
+ return Scaffold(
647
+ body: Stack(
648
+ children: [
649
+ LiveCoreWidget(controller: _controller),
650
+ ],
651
+ ),
652
+ );
653
+ }
654
+ }
655
+ ```
656
+ 3. **Log out of live stream**
657
+
658
+
659
+ When the audience exits the live streaming room, they need to call the **LiveListStore**'s `leaveLive` API to exit the live stream. The SDK will automatically stop pulling streams and leave the room.
660
+
661
+ ``` java
662
+ import 'package:flutter/material.dart';
663
+ import 'package:atomic_x_core/atomicxcore.dart';
664
+
665
+ /// YourAudiencePage represents your audience viewing webpage
666
+ class YourAudiencePage extends StatefulWidget {
667
+ final String liveId;
668
+
669
+ const YourAudiencePage({super.key, required this.liveId});
670
+
671
+ @override
672
+ State<YourAudiencePage> createState() => _YourAudiencePageState();
673
+ }
674
+
675
+ class _YourAudiencePageState extends State<YourAudiencePage> {
676
+ // ... Other code ...
677
+
678
+ End live stream
679
+ Future<void> _leaveLive() async {
680
+ final result = await LiveListStore.shared.leaveLive();
681
+
682
+ if (result.isSuccess) {
683
+ debugPrint("leaveLive success");
684
+ } else {
685
+ debugPrint("leaveLive error: ${result.errorMessage}");
686
+ }
687
+ }
688
+
689
+ // ... Other code ...
690
+ }
691
+ ```
692
+
693
+ ### Step 3: Listen for Live Events
694
+
695
+ After the audience joins the live room, you also need to process some passive events inside the room. For example, the Anchor proactively ends the live stream, or the audience is kicked out of the room due to rule violation. If these events are not listened to, the client UI may stay on a black screen webpage, affecting user experience.
696
+
697
+ You can achieve event monitoring through `LiveListListener`.
698
+ ``` java
699
+ import 'package:flutter/material.dart';
700
+ import 'package:atomic_x_core/atomicxcore.dart';
701
+
702
+ /// YourAudiencePage represents your audience viewing webpage
703
+ class YourAudiencePage extends StatefulWidget {
704
+ final String liveId;
705
+
706
+ const YourAudiencePage({super.key, required this.liveId});
707
+
708
+ @override
709
+ State<YourAudiencePage> createState() => _YourAudiencePageState();
710
+ }
711
+
712
+ class _YourAudiencePageState extends State<YourAudiencePage> {
713
+ late final LiveCoreController _controller;
714
+ // 1. Define LiveListListener to manage event monitoring
715
+ late final LiveListListener _liveListListener;
716
+
717
+ @override
718
+ void initState() {
719
+ super.initState();
720
+ _controller = LiveCoreController.create();
721
+ _controller.setLiveID(widget.liveId);
722
+ // 2. Listen to Live Event
723
+ _setupLiveEventListener();
724
+ // 3. Enter live room
725
+ _joinLive();
726
+ }
727
+
728
+ // 4. Add a method to set up event listening
729
+ void _setupLiveEventListener() {
730
+ _liveListListener = LiveListListener(
731
+ onLiveEnded: (liveID, reason, message) {
732
+ // Listen to live streaming end
733
+ debugPrint("Live ended. liveID: $liveID, reason: ${reason.value}, message: $message");
734
+ // Handle the logic for exiting a live streaming room herein, such as closing current page
735
+ // if (mounted) Navigator.of(context).pop();
736
+ },
737
+ onKickedOutOfLive: (liveID, reason, message) {
738
+ // Listen to being kicked out of live stream
739
+ debugPrint("Kicked out of live. liveID: $liveID, reason: ${reason.value}, message: $message");
740
+ // Handle the logic for exiting a live streaming room herein
741
+ // if (mounted) Navigator.of(context).pop();
742
+ },
743
+ );
744
+ LiveListStore.shared.addLiveListListener(_liveListListener);
745
+ }
746
+
747
+ Future<void> _joinLive() async {
748
+ final result = await LiveListStore.shared.joinLive(widget.liveId);
749
+ if (result.isSuccess) {
750
+ debugPrint("joinLive success");
751
+ } else {
752
+ debugPrint("joinLive error: ${result.errorMessage}");
753
+ }
754
+ }
755
+
756
+ Future<void> _leaveLive() async {
757
+ final result = await LiveListStore.shared.leaveLive();
758
+ if (result.isSuccess) {
759
+ debugPrint("leaveLive success");
760
+ } else {
761
+ debugPrint("leaveLive error: ${result.errorMessage}");
762
+ }
763
+ }
764
+
765
+ @override
766
+ void dispose() {
767
+ // 5. Remove event listening
768
+ LiveListStore.shared.removeLiveListListener(_liveListListener);
769
+ super.dispose();
770
+ }
771
+
772
+ @override
773
+ Widget build(BuildContext context) {
774
+ return Scaffold(
775
+ body: Stack(
776
+ children: [
777
+ LiveCoreWidget(controller: _controller),
778
+ ],
779
+ ),
780
+ );
781
+ }
782
+ }
783
+ ```
784
+
785
+ ### Run and Test
786
+
787
+ After integrating `LiveCoreWidget`, you will obtain a pure video rendering view with complete live streaming business capability but no interactive UI. You can refer to the next chapter [Enrich Live-Streaming Scenarios](https://write.woa.com/#.E4.B8.B0.E5.AF.8C.E7.9B.B4.E6.92.AD.E5.9C.BA.E6.99.AF) to refine live broadcasting scenarios.
788
+ <table>
789
+ <tr>
790
+ <td rowspan="1" colSpan="1" ></td>
791
+
792
+ <td rowspan="1" colSpan="1" >**Dynamic Grid Layout**</td>
793
+
794
+ <td rowspan="1" colSpan="1" >**Floating Window Layout**</td>
795
+
796
+ <td rowspan="1" colSpan="1" >**Fixed Grid Layout**</td>
797
+ </tr>
798
+
799
+ <tr>
800
+ <td rowspan="1" colSpan="1" >Template ID</td>
801
+
802
+ <td rowspan="1" colSpan="1" >600</td>
803
+
804
+ <td rowspan="1" colSpan="1" >601</td>
805
+
806
+ <td rowspan="1" colSpan="1" >800</td>
807
+ </tr>
808
+
809
+ <tr>
810
+ <td rowspan="1" colSpan="1" >Description</td>
811
+
812
+ <td rowspan="1" colSpan="1" >Default layout; grid size adjusts dynamically based on number of co-hosts.</td>
813
+
814
+ <td rowspan="1" colSpan="1" >Co-hosts are displayed as floating windows.</td>
815
+
816
+ <td rowspan="1" colSpan="1" >Fixed number of co-hosts; each occupies a fixed grid.</td>
817
+ </tr>
818
+
819
+ <tr>
820
+ <td rowspan="1" colSpan="1" >Example</td>
821
+
822
+ <td rowspan="1" colSpan="1" ><br>![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100043650599/6a730a2ecc0d11f084a45254005ef0f7.png)</td>
823
+
824
+ <td rowspan="1" colSpan="1" ><br>![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100043650599/71c322e9cc0d11f084a45254005ef0f7.png)</td>
825
+
826
+ <td rowspan="1" colSpan="1" ><br>![](https://write-document-release-1258344699.cos.ap-guangzhou.tencentcos.cn/100043650599/a1855792cc0d11f0ae4f52540099c741.png)</td>
827
+ </tr>
828
+ </table>
829
+
830
+
831
+ ## Enrich Live Streaming Scenarios
832
+
833
+ Once the basic live streaming functionality is in place, refer to the following guides to add interactive features to your live stream:
834
+ <table>
835
+ <tr>
836
+ <td rowspan="1" colSpan="1" >**Feature**</td>
837
+
838
+ <td rowspan="1" colSpan="1" >**Description**</td>
839
+
840
+ <td rowspan="1" colSpan="1" >**Store**</td>
841
+
842
+ <td rowspan="1" colSpan="1" >**Implementation Guide**</td>
843
+ </tr>
844
+
845
+ <tr>
846
+ <td rowspan="1" colSpan="1" >**Enable Audience Audio/Video Co-hosting**</td>
847
+
848
+ <td rowspan="1" colSpan="1" >Audience can apply to join the host and interact via real-time video.</td>
849
+
850
+ <td rowspan="1" colSpan="1" >[CoGuestStore](https://tencent-rtc.github.io/TUIKit_Flutter/api_live_co_guest_store/CoGuestStore-class.html)</td>
851
+
852
+ <td rowspan="1" colSpan="1" >[Implementation Guide](https://write.woa.com/document/200455653984186368)</td>
853
+ </tr>
854
+
855
+ <tr>
856
+ <td rowspan="1" colSpan="1" >**Enable Host Cross-room PK**</td>
857
+
858
+ <td rowspan="1" colSpan="1" >Hosts from different rooms can connect for interaction or PK.</td>
859
+
860
+ <td rowspan="1" colSpan="1" >[CoHostStore](https://tencent-rtc.github.io/TUIKit_Flutter/api_live_co_host_store/CoHostStore-class.html) / [BattleStore](https://tencent-rtc.github.io/TUIKit_Flutter/api_live_battle_store/BattleStore-class.html)</td>
861
+
862
+ <td rowspan="1" colSpan="1" >[Implementation Guide](https://write.woa.com/document/200455659512819712)</td>
863
+ </tr>
864
+
865
+ <tr>
866
+ <td rowspan="1" colSpan="1" >**Add Bullet Chat Feature**</td>
867
+
868
+ <td rowspan="1" colSpan="1" >Audience can send and receive real-time text messages in the live room.</td>
869
+
870
+ <td rowspan="1" colSpan="1" >[BarrageStore](https://tencent-rtc.github.io/TUIKit_Flutter/api_barrage_barrage_store/BarrageStore-class.html)</td>
871
+
872
+ <td rowspan="1" colSpan="1" >[Implementation Guide](https://write.woa.com/document/200455665056206848)</td>
873
+ </tr>
874
+
875
+ <tr>
876
+ <td rowspan="1" colSpan="1" >**Build a Gift Giving System**</td>
877
+
878
+ <td rowspan="1" colSpan="1" >Audience can send virtual gifts to the host, increasing engagement and fun.</td>
879
+
880
+ <td rowspan="1" colSpan="1" >[GiftStore](https://tencent-rtc.github.io/TUIKit_Flutter/api_gift_gift_store/GiftStore-class.html)</td>
881
+
882
+ <td rowspan="1" colSpan="1" >[Implementation Guide](https://write.woa.com/document/195925512239050752)</td>
883
+ </tr>
884
+ </table>
885
+
886
+
887
+ ## API documentation
888
+ <table>
889
+ <tr>
890
+ <td rowspan="1" colSpan="1" >**Store/Component**</td>
891
+
892
+ <td rowspan="1" colSpan="1" >**Feature Description**</td>
893
+
894
+ <td rowspan="1" colSpan="1" >**API Reference**</td>
895
+ </tr>
896
+
897
+ <tr>
898
+ <td rowspan="1" colSpan="1" >**LiveCoreWidget**</td>
899
+
900
+ <td rowspan="1" colSpan="1" >The core view component for live video stream display and interaction: responsible for video stream rendering and view widget processing, supporting scenarios like live streaming, audience co-broadcasting, and anchor connection.</td>
901
+
902
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_view_live_live_core_widget/LiveCoreWidget-class.html)</td>
903
+ </tr>
904
+
905
+ <tr>
906
+ <td rowspan="1" colSpan="1" >**LiveCoreController**</td>
907
+
908
+ <td rowspan="1" colSpan="1" >The controller of LiveCoreWidget: used to set up the live streaming ID, control the preview, and perform other operations.</td>
909
+
910
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_view_live_live_core_widget/LiveCoreController-class.html)</td>
911
+ </tr>
912
+
913
+ <tr>
914
+ <td rowspan="1" colSpan="1" >**LiveListStore**</td>
915
+
916
+ <td rowspan="1" colSpan="1" >Full lifecycle management of live streaming room: create/join/leave/terminate room, query room list, modify live information (name, notice), listen to live status (for example, being kicked out, ended).</td>
917
+
918
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_live_live_list_store/LiveListStore-class.html)</td>
919
+ </tr>
920
+
921
+ <tr>
922
+ <td rowspan="1" colSpan="1" >**DeviceStore**</td>
923
+
924
+ <td rowspan="1" colSpan="1" >Audio/Video device control: microphone (on/off, volume), camera (on/off, switchover, video quality), screen sharing, monitor device status in real time.</td>
925
+
926
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_device_device_store/DeviceStore-class.html)</td>
927
+ </tr>
928
+
929
+ <tr>
930
+ <td rowspan="1" colSpan="1" >**CoGuestStore**</td>
931
+
932
+ <td rowspan="1" colSpan="1" >Audience co-broadcasting management: join microphone application/invite/grant/deny, attendee permission control (microphone/camera), state synchronization.</td>
933
+
934
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_live_co_guest_store/CoGuestStore-class.html)</td>
935
+ </tr>
936
+
937
+ <tr>
938
+ <td rowspan="1" colSpan="1" >**CoHostStore**</td>
939
+
940
+ <td rowspan="1" colSpan="1" >Anchor cross-room connection: supports multiple layout templates (dynamic grid, etc.), initiate/accept/reject connection, and manage interactive co-broadcasting with Anchors.</td>
941
+
942
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_live_co_host_store/CoHostStore-class.html)</td>
943
+ </tr>
944
+
945
+ <tr>
946
+ <td rowspan="1" colSpan="1" >**BattleStore**</td>
947
+
948
+ <td rowspan="1" colSpan="1" >Anchor PK battle: trigger PK (duration configuration/competitor), manage PK status (start/End), synchronize score, listen to battle results.</td>
949
+
950
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_live_battle_store/BattleStore-class.html)</td>
951
+ </tr>
952
+
953
+ <tr>
954
+ <td rowspan="1" colSpan="1" >**GiftStore**</td>
955
+
956
+ <td rowspan="1" colSpan="1" >Gift interaction: get gift list, send/receive gifts, listen to gift events (including sender, gift details).</td>
957
+
958
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_gift_gift_store/GiftStore-class.html)</td>
959
+ </tr>
960
+
961
+ <tr>
962
+ <td rowspan="1" colSpan="1" >**BarrageStore**</td>
963
+
964
+ <td rowspan="1" colSpan="1" >Danmaku function: send text/custom barrage item, maintain bullet screen list, monitor bullet screen status in real time.</td>
965
+
966
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_barrage_barrage_store/BarrageStore-class.html)</td>
967
+ </tr>
968
+
969
+ <tr>
970
+ <td rowspan="1" colSpan="1" >**LikeStore**</td>
971
+
972
+ <td rowspan="1" colSpan="1" >Like interaction: send likes, listen to like events, sync total likes.</td>
973
+
974
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_live_like_store/LikeStore-class.html)</td>
975
+ </tr>
976
+
977
+ <tr>
978
+ <td rowspan="1" colSpan="1" >**LiveAudienceStore**</td>
979
+
980
+ <td rowspan="1" colSpan="1" >Audience management: get real-time audience list (ID/name/avatar), check audience size, listen to Enter/Exit Event.</td>
981
+
982
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_live_live_audience_store/LiveAudienceStore-class.html)</td>
983
+ </tr>
984
+
985
+ <tr>
986
+ <td rowspan="1" colSpan="1" >**AudioEffectStore**</td>
987
+
988
+ <td rowspan="1" colSpan="1" >Audio effects: voice-changing (child voice/male voice), reverberation (KTV), ear monitoring adjustment, switch effects in real time.</td>
989
+
990
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_device_audio_effect_store/AudioEffectStore-class.html)</td>
991
+ </tr>
992
+
993
+ <tr>
994
+ <td rowspan="1" colSpan="1" >**BaseBeautyStore**</td>
995
+
996
+ <td rowspan="1" colSpan="1" >Basic beauty filter: adjust skin smoothing/whitening/rosy (0-100 levels), reset beauty effect status, synchronize effect parameters.</td>
997
+
998
+ <td rowspan="1" colSpan="1" >[API documentation](https://tencent-rtc.github.io/TUIKit_Flutter/api_device_base_beauty_store/BaseBeautyStore-class.html)</td>
999
+ </tr>
1000
+ </table>
1001
+
1002
+
1003
+ ## FAQs
1004
+
1005
+ ### Why is the screen black with no video after the host calls createLive or the audience calls joinLive?
1006
+ - **Check setLiveID**: Please ensure the correct `liveID` has been set for the `LiveCoreController` instance before calling the live streaming or viewing API.
1007
+
1008
+ - **Check device permission**: Please ensure the app has obtained system usage permission for the camera and microphone.
1009
+
1010
+ - **Check anchor side**: Whether the Anchor side normally called `DeviceStore.shared.openLocalCamera(true)` to open the camera.
1011
+
1012
+ - **Check the network**: Please check whether the device network connection is normal.
1013
+
1014
+
1015
+ ### How to Request Permission in a Flutter Project?
1016
+
1017
+ You can use the `permission_handler` plug-in to request camera and mic permission:
1018
+ ``` java
1019
+ import 'package:permission_handler/permission_handler.dart';
1020
+
1021
+ Future<void> requestPermissions() async {
1022
+ await [
1023
+ Permission.camera,
1024
+ Permission.microphone,
1025
+ ].request();
1026
+ }
1027
+ ```
1028
+
1029
+ ### How to Handle Page Lifecycle in Flutter?
1030
+
1031
+ It is recommended to clean up resources in the `dispose` method, such as removing event listeners:
1032
+ ``` java
1033
+ @override
1034
+ void dispose() {
1035
+ LiveListStore.shared.removeLiveListListener(_liveListListener);
1036
+ super.dispose();
1037
+ }
1038
+ ```
1039
+
1040
+
1041
+