LiveCoreWidget 是一个跨平台的视频直播核心组件,提供开播、观看、连麦和主播 PK 等关键能力。该组件通过挂件机制,支持在视频区域实时显示用户名、等级、PK 进度条等自定义信息。本文档指导开发者通过实现接口,在 Flutter 平台上快速定制专属的视频挂件 Widget 。连线视频挂件 | PK 视频挂件 |
![]() | ![]() |
LiveCoreWidget 通过 VideoWidgetBuilder 代理支持自定义视图渲染。当业务场景发生变化(例如有人上麦、开始 PK )时,LiveCoreWidget 会调用代理方法询问应该显示的视图。开发者实现对应的接口方法并返回自定义的 Widget 实例即可。回调 | 描述 | 对应业务场景 |
CoGuestWidgetBuilder | 创建观众连麦的挂件视图。 | 观众连麦、邀请上麦 |
CoHostWidgetBuilder | 创建跨房连麦(主播连线)的挂件视图。 | 主播连线 |
BattleWidgetBuilder | 创建 PK 场景中单个用户的挂件视图(例如头像、分数)。 | 主播 PK |
BattleContainerWidgetBuilder | 创建 PK 场景的整体容器视图(例如背景、分数 PK 条)。 | 主播 PK |

import 'package:flutter/material.dart';class CustomInfoWidget extends StatelessWidget {final String name;final bool isMuted;const CustomInfoWidget({super.key,required this.name,required this.isMuted,});@overrideWidget build(BuildContext context) {return Stack(children: [Text(name),if (isMuted) const Icon(Icons.mic_off),// 此处省略具体布局参数设置代码],);}}
import 'package:flutter/material.dart';class EmptySeatWidget extends StatelessWidget {const EmptySeatWidget({super.key});@overrideWidget build(BuildContext context) {return Column(mainAxisAlignment: MainAxisAlignment.center,children: const [Icon(Icons.add),Text('邀请连线'),// 此处省略图片资源加载和布局参数设置代码],);}}
import 'package:flutter/material.dart';class CustomAvatarWidget extends StatelessWidget {final String avatarURL;const CustomAvatarWidget({super.key,required this.avatarURL,});@overrideWidget build(BuildContext context) {// 实际项目中可使用 CachedNetworkImage 等图片加载库加载 avatarURLreturn Image.network(avatarURL);// 此处省略具体布局参数设置代码}}
VideoWidgetBuilder 类的回调方法 coGuestWidgetBuilder (观众连线)和 coHostWidgetBuilder (主播连线),返回刚才创建的自定义视图。VideoWidgetBuilder 中的 coGuestWidgetBuilder 回调方法,返回观众连线视频挂件。import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';VideoWidgetBuilder(coGuestWidgetBuilder: (context, seatInfo, viewLayer) {final isUserOnSeat = seatInfo.userInfo.userID.isNotEmpty;if (viewLayer == ViewLayer.foreground) {if (isUserOnSeat) {// 非空麦位,返回自定义的前景视图return CustomInfoWidget(name: seatInfo.userInfo.userName, isMuted: seatInfo.userInfo.microphoneStatus == DeviceStatus.off);} else {// 空麦位,返回自定义空麦位视图return EmptySeatWidget();}} else {if (isUserOnSeat) {// 返回自定义的背景视图(未开摄像头时显示)return CustomAvatarWidget(avatarURL: seatInfo.userInfo.avatarURL);} else {return SizedBox.shrink();}}})
VideoWidgetBuilder 中的 coHostWidgetBuilder 回调方法,返回主播连线的视频挂件。import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';VideoWidgetBuilder(coHostWidgetBuilder: (context, seatInfo, viewLayer) {final isUserOnSeat = seatInfo.userInfo.userID.isNotEmpty;if (viewLayer == ViewLayer.foreground) {if (isUserOnSeat) {// 返回自定义的前景视图,可以返回与观众连线不同的样式return CustomInfoWidget(name: seatInfo.userInfo.userName, isMuted: seatInfo.userInfo.microphoneStatus == DeviceStatus.off);} else {// 返回自定义空麦位视图,可以返回与观众连线不同的样式return EmptySeatWidget();}} else {if (isUserOnSeat) {// 返回自定义的背景视图(未开摄像头时显示),可以返回与观众连线不同的样式return CustomAvatarWidget(avatarURL: seatInfo.userInfo.avatarURL);} else {return SizedBox.shrink();}}})
参数 | 类型 | 说明 |
seatInfo | SeatInfo | 麦位信息对象,包含麦上用户的详细信息。 |
seatInfo.userInfo.userName | String | 麦上用户的昵称。 |
seatInfo.userInfo.avatarURL | String | 麦上用户的头像 URL。 |
seatInfo.userInfo.microphoneStatus | DeviceStatus | 麦上用户的麦克风状态。 |
seatInfo.userInfo.cameraStatus | DeviceStatus | 麦上用户的摄像头状态。 |
viewLayer | ViewLayer | 视图层级枚举。 .foreground 表示前景挂件视图,始终显示在视频画面的最上层。.background 表示背景挂件视图,位于前景视图下层,仅在对应用户没有视频流(例如未开摄像头)的情况下显示,通常用于展示用户的默认头像或占位图。 |
PK 是直播中互动性最强的环节。在 PK 过程中,画面通常根据参与人数被分割为几部分。开发者需要在画面上方或中间区域添加 PK 专用的 UI ,分数 PK 条、每个麦位上的分数显示、倒计时动画或“VS”特效图标,以营造紧张的竞技氛围。
PK 场景通常需要两类视图:import 'package:flutter/material.dart';// 示例:单人分数条class MyBattleScoreWidget extends StatelessWidget {const MyBattleScoreWidget({super.key});@overrideWidget build(BuildContext context) {// 内部实现分数显示逻辑return const Placeholder();}}// 示例:全局 VS 面板class MyBattleContainer extends StatelessWidget {const MyBattleContainer({super.key});@overrideWidget build(BuildContext context) {// 内部实现倒计时和 VS 动画逻辑return const Placeholder();}}
VideoWidgetBuilder 中,实现剩余的 PK 视图方法。import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';VideoWidgetBuilder(// 省略 createCoGuestView / createCoHostView 实现...// 1. 创建【PK 单人信息】挂件 (显示在每个主播视频上方)battleWidgetBuilder: (context, seatInfo) {return MyBattleScoreWidget();}, // 2. 创建【PK 全局容器】挂件 (显示在整个视频区域上方)battleContainerWidgetBuilder: (context) {return MyBattleContainer();})
VideoWidgetBuilder 注入到直播核心流程中。LiveCoreWidget 并通过 videoWidgetBuilder 设置VideoWidgetBuilder。import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';class AnchorWidget extends StatefulWidget {const AnchorWidget({super.key});@overrideState<AnchorWidget> createState() => _AnchorWidgetState();}class _AnchorWidgetState extends State<AnchorWidget> {// 假设此处已获取 liveInfofinal LiveInfo liveInfo = LiveInfo();late final LiveCoreController _liveCoreController;@overridevoid initState() {super.initState();// 1. 初始化 LiveCoreWidget 的控制器 LiveCoreController_liveCoreController = LiveCoreController.create(CoreViewType.pushView);_liveCoreController.setLiveID(liveInfo.liveID);}@overrideWidget build(BuildContext context) {return LiveCoreWidget(// 2. 将核心视频组件放到父 Widget 中并传入控制器controller: _liveCoreController,// 3. 传入前面定义的 VideoWidgetBuilder的 各个回调方法videoWidgetBuilder: VideoWidgetBuilder(coGuestWidgetBuilder: (context, seatInfo, viewLayer) {// 传入前面步骤中定义的 coGuestWidgetBuilder 组件实现return Placeholder();}, coHostWidgetBuilder: (context, seatInfo, viewLayer) {// 传入前面步骤中定义的 coHostWidgetBuilder 组件实现return Placeholder();}, battleWidgetBuilder: (context, seatInfo) {// 传入前面步骤中定义的 battleWidgetBuilder 组件实现return Placeholder();}, battleContainerWidgetBuilder: (context) {// 传入前面步骤中定义的 battleContainerWidgetBuilder 组件实现return Placeholder();}));}}
LiveCoreController的 CoreViewType 类型修改为 CoreViewType.pushView 即可。import 'package:flutter/material.dart';import 'package:atomic_x_core/atomicxcore.dart';class AudienceWidget extends StatefulWidget {const AudienceWidget({super.key});@overrideState<AudienceWidget> createState() => _AudienceWidgetState();}class _AudienceWidgetState extends State<AudienceWidget> {// 假设此处已获取 liveInfofinal LiveInfo liveInfo = LiveInfo();late final LiveCoreController _liveCoreController;@overridevoid initState() {super.initState();// 1. 初始化 LiveCoreWidget 的控制器 LiveCoreController_liveCoreController = LiveCoreController.create(CoreViewType.pushView);_liveCoreController.setLiveID(liveInfo.liveID);}@overrideWidget build(BuildContext context) {return LiveCoreWidget(// 2. 将核心视频组件放到父 Widget 中并传入控制器controller: _liveCoreController,// 3. 传入前面定义的 VideoWidgetBuilder的 各个回调方法videoWidgetBuilder: VideoWidgetBuilder(coGuestWidgetBuilder: (context, seatInfo, viewLayer) {// 传入前面步骤中定义的 coGuestWidgetBuilder 组件实现return Placeholder();}, coHostWidgetBuilder: (context, seatInfo, viewLayer) {// 传入前面步骤中定义的 coHostWidgetBuilder 组件实现return Placeholder();}, battleWidgetBuilder: (context, seatInfo) {// 传入前面步骤中定义的 battleWidgetBuilder 组件实现return Placeholder();}, battleContainerWidgetBuilder: (context) {// 传入前面步骤中定义的 battleContainerWidgetBuilder 组件实现return Placeholder();}));}}
PK 等复杂场景时,您会发现 SeatInfo 只提供了基础麦位信息。如果您需要获取实时倒计时、PK 分数等业务数据,就需要在您的自定义视图中对接 AtomicXCore 中的核心数据。VideoWidgetBuilder 的 coGuestWidgetBuilder, 将其返回的 Widget 替换成您的 CustomWidget 即可。.foreground) 始终位于视频层之上,所以您要检查您的 CustomWidget 是否位于 .foreground 层,同时确保它的父视图没有禁用交互。文档反馈