产品动态
产品近期公告
关于 TRTC Live 正式上线的公告
关于TRTC Conference 正式版上线的公告
Conference 商业化版本即将推出
关于多人音视频 Conference 开启内测公告
关于音视频通话 Call 正式版上线的公告
关于腾讯云音视频终端 SDK 播放升级及新增授权校验的公告
关于 TRTC 应用订阅套餐服务上线的相关说明

requestHostConnection 方法。import 'package:atomic_x_core/atomicxcore.dart';// 主播A的页面class AnchorAPage extends StatefulWidget {final String liveId;const AnchorAPage({Key? key, required this.liveId}) : super(key: key);@overrideState<AnchorAPage> createState() => _AnchorAPageState();}class _AnchorAPageState extends State<AnchorAPage> {late final CoHostStore _coHostStore;late final CoHostListener _coHostListener;@overridevoid initState() {super.initState();_coHostStore = CoHostStore.create(widget.liveId);_setupListeners();}// 用户点击"连线"按钮,并选择了主播BFuture<void> inviteHostB(String targetHostLiveId) async {final layout = CoHostLayoutTemplate.hostDynamicGrid; // 选择一个布局模板const timeout = 30; // 邀请超时时间(秒)final result = await _coHostStore.requestHostConnection(targetHostLiveID: targetHostLiveId,layoutTemplate: layout,timeout: timeout,);if (result.isSuccess) {print('连线邀请已发送,等待对方处理...');} else {print('邀请发送失败: ${result.errorMessage}');}}@overridevoid dispose() {_coHostStore.removeCoHostListener(_coHostListener);super.dispose();}}
CoHostListener,您可以接收到主播 B 的处理结果。// 在 _AnchorAPageState 初始化时设置监听void _setupListeners() {_coHostListener = CoHostListener(onCoHostRequestAccepted: (invitee) {print('主播 ${invitee.userName} 同意了你的连线邀请');},onCoHostRequestRejected: (invitee) {print('主播 ${invitee.userName} 拒绝了你的邀请');},onCoHostRequestTimeout: (inviter, invitee) {print('邀请超时,对方未回应');},onCoHostUserJoined: (userInfo) {print('主播 ${userInfo.userName} 已加入连线');},onCoHostUserLeft: (userInfo) {print('主播 ${userInfo.userName} 已离开连线');},);_coHostStore.addCoHostListener(_coHostListener);}
CoHostListener,主播B可以监听到来自主播 A 的邀请。import 'package:atomic_x_core/atomicxcore.dart';// 主播B的页面class AnchorBPage extends StatefulWidget {final String liveId;const AnchorBPage({Key? key, required this.liveId}) : super(key: key);@overrideState<AnchorBPage> createState() => _AnchorBPageState();}class _AnchorBPageState extends State<AnchorBPage> {late final CoHostStore _coHostStore;late final CoHostListener _coHostListener;@overridevoid initState() {super.initState();_coHostStore = CoHostStore.create(widget.liveId);_setupListeners();}// 在初始化时设置监听void _setupListeners() {_coHostListener = CoHostListener(onCoHostRequestReceived: (inviter, extensionInfo) {print('收到主播 ${inviter.userName} 的连线邀请');// _showInvitationDialog(inviter);},);_coHostStore.addCoHostListener(_coHostListener);}@overridevoid dispose() {_coHostStore.removeCoHostListener(_coHostListener);super.dispose();}}
// _AnchorBPageState 的一部分Future<void> acceptInvitation(String fromHostLiveId) async {final result = await _coHostStore.acceptHostConnection(fromHostLiveId);if (result.isSuccess) {print('已接受连线邀请');} else {print('接受连线失败: ${result.errorMessage}');}}Future<void> rejectInvitation(String fromHostLiveId) async {final result = await _coHostStore.rejectHostConnection(fromHostLiveId);if (result.isSuccess) {print('已拒绝连线邀请');} else {print('拒绝连线失败: ${result.errorMessage}');}}
requestBattle 方法。// _AnchorAPageState 的一部分late final BattleStore _battleStore;late final BattleListener _battleListener;@overridevoid initState() {super.initState();_coHostStore = CoHostStore.create(widget.liveId);_battleStore = BattleStore.create(widget.liveId);_setupListeners();_setupBattleListeners();}Future<void> startPK(String opponentUserId) async {final config = BattleConfig(duration: 300); // PK 持续 5 分钟final result = await _battleStore.requestBattle(config: config,userIDList: [opponentUserId],timeout: 30,);if (result.isSuccess) {print('PK 请求已发送,battleID: ${result.battleID}');} else {print('PK 请求失败: ${result.errorMessage}');}}
BattleListener 监听 PK 的开始、结束等关键事件。// 在 _AnchorAPageState 的 _setupBattleListeners 方法中添加void _setupBattleListeners() {_battleListener = BattleListener(onBattleStarted: (battleInfo, inviter, invitees) {print('PK 开始');},onBattleEnded: (battleInfo, reason) {print('PK 结束,原因: $reason');},onUserJoinBattle: (battleID, battleUser) {print('用户 ${battleUser.userName} 加入了 PK');},onUserExitBattle: (battleID, battleUser) {print('用户 ${battleUser.userName} 退出了 PK');},);_battleStore.addBattleListener(_battleListener);}@overridevoid dispose() {_coHostStore.removeCoHostListener(_coHostListener);_battleStore.removeBattleListener(_battleListener);super.dispose();}
BattleListener 监听到 PK 邀请。// 在 _AnchorBPageState 的 _setupBattleListeners 方法中添加void _setupBattleListeners() {_battleListener = BattleListener(onBattleRequestReceived: (battleId, inviter, invitee) {print('收到主播 ${inviter.userName} 的PK挑战');// 弹出对话框,让主播B选择"接受"或"拒绝"// _showPKChallengeDialog(battleId);},onBattleStarted: (battleInfo, inviter, invitees) {print('PK 开始');},onBattleEnded: (battleInfo, reason) {print('PK 结束');},);_battleStore.addBattleListener(_battleListener);}
// _AnchorBPageState 的一部分// 用户点击"接受挑战"Future<void> acceptPK(String battleId) async {final result = await _battleStore.acceptBattle(battleId);if (result.isSuccess) {print('已接受 PK 挑战');} else {print('接受 PK 失败: ${result.errorMessage}');}}// 用户点击"拒绝挑战"Future<void> rejectPK(String battleId) async {final result = await _battleStore.rejectBattle(battleId);if (result.isSuccess) {print('已拒绝 PK 挑战');} else {print('拒绝 PK 失败: ${result.errorMessage}');}}

LiveCoreWidget 的 VideoWidgetBuilder 参数提供的"插槽"能力,在视频流画面上添加自定义视图,用于显示昵称、头像、PK 进度条等信息,或在他们关闭摄像头时提供占位图,以优化视觉体验。
import 'package:flutter/material.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';/// 自定义的连线主播信息悬浮视图(前景)class CustomCoHostForegroundView extends StatelessWidget {final SeatFullInfo seatInfo;const CustomCoHostForegroundView({Key? key,required this.seatInfo,}) : super(key: key);@overrideWidget build(BuildContext context) {return Container(color: Colors.transparent,child: Align(alignment: Alignment.bottomLeft,child: Container(margin: const EdgeInsets.all(5.0),padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),decoration: BoxDecoration(color: Colors.black.withOpacity(0.5),borderRadius: BorderRadius.circular(12),),child: Text(seatInfo.userInfo.userName,style: const TextStyle(color: Colors.white,fontSize: 14,),),),),);}}
import 'package:flutter/material.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';/// 自定义的连线主播头像占位视图(背景)class CustomCoHostBackgroundView extends StatelessWidget {final SeatFullInfo seatInfo;const CustomCoHostBackgroundView({Key? key,required this.seatInfo,}) : super(key: key);@overrideWidget build(BuildContext context) {final avatarUrl = seatInfo.userInfo.avatarUrl;return Container(decoration: BoxDecoration(color: Colors.grey[800],),child: Center(child: Column(mainAxisSize: MainAxisSize.min,children: [ClipOval(child: avatarUrl.isNotEmpty? Image.network(avatarUrl,width: 60,height: 60,fit: BoxFit.cover,errorBuilder: (context, error, stackTrace) {return _buildDefaultAvatar();},): _buildDefaultAvatar(),),const SizedBox(height: 8),Text(seatInfo.userInfo.userName,style: const TextStyle(color: Colors.white,fontSize: 12,),),],),),);}Widget _buildDefaultAvatar() {return Container(width: 60,height: 60,color: Colors.grey,child: const Icon(Icons.person, size: 40, color: Colors.white),);}}
VideoWidgetBuilder 的 coHostWidgetBuilder 回调构建自定义视图,根据 viewLayer 的值返回对应的视图。import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';/// 带有自定义连线视图的直播页面class CustomCoHostLiveWidget extends StatefulWidget {final String liveId;const CustomCoHostLiveWidget({Key? key,required this.liveId,}) : super(key: key);@overrideState<CustomCoHostLiveWidget> createState() => _CustomCoHostLiveWidgetState();}class _CustomCoHostLiveWidgetState extends State<CustomCoHostLiveWidget> {late LiveCoreController _controller;@overridevoid initState() {super.initState();_controller = LiveCoreController.create();_controller.setLiveID(widget.liveId);}@overridevoid dispose() {_controller.dispose();super.dispose();}/// 构建连线主播的自定义视图Widget _buildCoHostWidget(BuildContext context,SeatFullInfo seatFullInfo,ViewLayer viewLayer,) {if (viewLayer == ViewLayer.foreground) {// 前景层:始终显示在视频画面的最上层,用于显示昵称等信息return CustomCoHostForegroundView(seatInfo: seatFullInfo);} else {// 背景层:仅在对应用户没有视频流时显示,用于显示头像占位图return CustomCoHostBackgroundView(seatInfo: seatFullInfo);}}@overrideWidget build(BuildContext context) {return Scaffold(body: LiveCoreWidget(controller: _controller,videoWidgetBuilder: VideoWidgetBuilder(coHostWidgetBuilder: _buildCoHostWidget,),),);}}
参数 | 类型 | 说明 |
seatFullInfo | SeatFullInfo | 麦位信息对象,包含麦上用户的详细信息 |
seatFullInfo.userInfo.userId | String | 麦上用户的 ID |
seatFullInfo.userInfo.userName | String | 麦上用户的昵称 |
seatFullInfo.userInfo.avatarUrl | String | 麦上用户的头像 URL |
viewLayer | ViewLayer | 视图层级枚举 ViewLayer.foreground 表示前景挂件视图,始终显示在视频画面的最上层ViewLayer.background 表示背景挂件视图,位于前景视图下层,仅在对应用户没有视频流(例如未开摄像头)的情况下显示,通常用于展示用户的默认头像或占位图 |

import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';/// 自定义 PK 用户视图class CustomBattleUserView extends StatefulWidget {final String liveId;final TUIBattleUser battleUser;const CustomBattleUserView({Key? key,required this.liveId,required this.battleUser,}) : super(key: key);@overrideState<CustomBattleUserView> createState() => _CustomBattleUserViewState();}class _CustomBattleUserViewState extends State<CustomBattleUserView> {late final BattleStore _battleStore;late final VoidCallback _scoreChangedListener = _onScoreChanged;int _score = 0;@overridevoid initState() {super.initState();_battleStore = BattleStore.create(widget.liveId);_subscribeBattleState();}/// 订阅 PK 分数变化void _subscribeBattleState() {_battleStore.battleState.battleScore.addListener(_scoreChangedListener);// 初始化分数_updateScore(_battleStore.battleState.battleScore.value);}void _onScoreChanged() {_updateScore(_battleStore.battleState.battleScore.value);}void _updateScore(Map<String, int> battleScore) {final score = battleScore[widget.battleUser.userId] ?? 0;if (mounted && score != _score) {setState(() {_score = score;});}}@overridevoid dispose() {_battleStore.battleState.battleScore.removeListener(_scoreChangedListener);super.dispose();}@overrideWidget build(BuildContext context) {return IgnorePointer(child: Align(alignment: Alignment.bottomRight,child: Container(margin: const EdgeInsets.all(5),padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),decoration: BoxDecoration(color: Colors.black.withOpacity(0.4),borderRadius: BorderRadius.circular(12),),child: Text('$_score',style: const TextStyle(color: Colors.white,fontSize: 14,fontWeight: FontWeight.bold,),),),),);}}
VideoWidgetBuilder 的 battleWidgetBuilder 回调构建自定义 PK 视图。import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';/// 带有自定义 PK 视图的直播页面class CustomBattleLiveWidget extends StatefulWidget {final String liveId;const CustomBattleLiveWidget({Key? key,required this.liveId,}) : super(key: key);@overrideState<CustomBattleLiveWidget> createState() => _CustomBattleLiveWidgetState();}class _CustomBattleLiveWidgetState extends State<CustomBattleLiveWidget> {late LiveCoreController _controller;@overridevoid initState() {super.initState();_controller = LiveCoreController.create();_controller.setLiveID(widget.liveId);}@overridevoid dispose() {_controller.dispose();super.dispose();}/// 构建 PK 用户的自定义视图Widget _buildBattleWidget(BuildContext context, TUIBattleUser battleUser) {return CustomBattleUserView(liveId: widget.liveId,battleUser: battleUser,);}@overrideWidget build(BuildContext context) {return Scaffold(body: LiveCoreWidget(controller: _controller,videoWidgetBuilder: VideoWidgetBuilder(battleWidgetBuilder: _buildBattleWidget,),),);}}
参数 | 类型 | 说明 |
battleUser | TUIBattleUser | PK 用户信息对象。 |
battleUser.roomId | String | PK 的房间 ID。 |
battleUser.userId | String | PK 用户 ID。 |
battleUser.userName | String | PK 用户昵称。 |
battleUser.avatarUrl | String | PK 用户头像地址。 |
battleUser.score | int | PK 分数。 |

VideoWidgetBuilder 的 battleContainerWidgetBuilder 回调构建自定义 PK 容器视图。import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';/// 带有自定义 PK 容器视图的直播页面class CustomBattleContainerLiveWidget extends StatefulWidget {final String liveId;const CustomBattleContainerLiveWidget({Key? key,required this.liveId,}) : super(key: key);@overrideState<CustomBattleContainerLiveWidget> createState() => _CustomBattleContainerLiveWidgetState();}class _CustomBattleContainerLiveWidgetState extends State<CustomBattleContainerLiveWidget> {late LiveCoreController _controller;@overridevoid initState() {super.initState();_controller = LiveCoreController.create();_controller.setLiveID(widget.liveId);}@overridevoid dispose() {_controller.dispose();super.dispose();}/// 构建 PK 容器视图Widget _buildBattleContainerWidget(BuildContext context) {// CustomBattleContainerView 是您自定义的 PK 全局视图return CustomBattleContainerView(liveId: widget.liveId);}@overrideWidget build(BuildContext context) {return Scaffold(body: LiveCoreWidget(controller: _controller,videoWidgetBuilder: VideoWidgetBuilder(battleContainerWidgetBuilder: _buildBattleContainerWidget,),),);}}
import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';import 'package:rtc_room_engine/rtc_room_engine.dart';/// 完整的自定义视图直播页面class FullCustomLiveWidget extends StatefulWidget {final String liveId;const FullCustomLiveWidget({Key? key,required this.liveId,}) : super(key: key);@overrideState<FullCustomLiveWidget> createState() => _FullCustomLiveWidgetState();}class _FullCustomLiveWidgetState extends State<FullCustomLiveWidget> {late LiveCoreController _controller;@overridevoid initState() {super.initState();_controller = LiveCoreController.create();_controller.setLiveID(widget.liveId);}@overridevoid dispose() {_controller.dispose();super.dispose();}/// 构建连线主播的自定义视图Widget _buildCoHostWidget(BuildContext context,SeatFullInfo seatFullInfo,ViewLayer viewLayer,) {if (viewLayer == ViewLayer.foreground) {return CustomCoHostForegroundView(seatInfo: seatFullInfo);} else {return CustomCoHostBackgroundView(seatInfo: seatFullInfo);}}/// 构建 PK 用户的自定义视图Widget _buildBattleWidget(BuildContext context, TUIBattleUser userInfo) {return CustomBattleUserView(liveId: widget.liveId,battleUser: userInfo,);}/// 构建 PK 容器视图Widget _buildBattleContainerWidget(BuildContext context) {return CustomBattleContainerView(liveId: widget.liveId);}@overrideWidget build(BuildContext context) {return Scaffold(body: LiveCoreWidget(controller: _controller,videoWidgetBuilder: VideoWidgetBuilder(coHostWidgetBuilder: _buildCoHostWidget,battleWidgetBuilder: _buildBattleWidget,battleContainerWidgetBuilder: _buildBattleContainerWidget,),),);}}
礼物类型 | 分数计算规则 | 示例 |
基础礼物 | 礼物价值 × 5 | 10元礼物 → 50分 |
中级礼物 | 礼物价值 × 8 | 50元礼物 → 400分 |
高级礼物 | 礼物价值 × 12 | 100元礼物 → 1200分 |
特效礼物 | 固定高分数 | 520元礼物 → 1314分 |

Store/Component | 功能描述 | API 文档 |
LiveCoreWidget | 直播视频流展示与交互的核心视图组件:负责视频流渲染和视图挂件处理,支持主播直播、观众连麦、主播连线等场景。 | |
LiveCoreController | LiveCoreWidget 的控制器:用于设置直播 ID、控制预览等操作。 | |
VideoWidgetBuilder | 视频视图适配器:用于自定义连线主播、PK 用户、PK 容器等场景的视频流挂件视图。 | |
DeviceStore | 音视频设备控制:麦克风(开关 / 音量)、摄像头(开关 / 切换 / 画质)、屏幕共享,设备状态实时监听。 | |
CoHostStore | 主播跨房连线:支持多布局模板(动态网格等),发起 / 接受 / 拒绝连线,连麦主播互动管理。 | |
BattleStore | 主播 PK 对战:发起 PK(配置时长 / 对手),管理 PK 状态(开始 / 结束),同步分数,监听对战结果。 |
targetHostLiveId 是否正确,并且对方直播间处于正常开播状态。VideoWidgetBuilder 添加的自定义视图的生命周期和事件?coHostWidgetBuilder、battleWidgetBuilder、battleContainerWidgetBuilder 回调返回视图的添加和移除,您无需手动处理。如果需要在自定义视图中处理用户交互(例如点击事件),请在创建视图时为其添加相应的事件即可。文档反馈