在本指南中,我们将学习如何为你的 RealtimeKit 会议体验添加自定义控制栏。
RealtimeKit UI Kit 提供了 RtkControlbar 组件作为默认的控制栏。
如果需要额外的控制项,请将 RtkControlbar 替换为单个 UI Kit 组件和自定义元素。
导入所需的组件和 React hook:
import { useRef } from "react";
import {
RtkFullscreenToggle,
RtkSettingsToggle,
RtkScreenShareToggle,
RtkMicToggle,
RtkCameraToggle,
RtkStageToggle,
RtkLeaveButton,
RtkMoreToggle,
RtkPipToggle,
RtkMuteAllButton,
RtkBreakoutRoomsToggle,
RtkRecordingToggle,
RtkChatToggle,
RtkPollsToggle,
RtkParticipantsToggle,
RtkPluginsToggle,
} from "@cloudflare/realtimekit-ui";在 构建自定义 UI 中的 RtkUIProvider 里,替换:
<RtkControlbar />为:
<div
style={{
display: "flex",
width: "100%",
padding: "8px 12px",
color: "white",
justifyContent: "space-between",
}}
>
<div
id="controlbar-left"
style={{ display: "flex", alignItems: "center", justifyContent: "center" }}
>
<RtkFullscreenToggle targetElement={fullScreenRef.current} />
<RtkSettingsToggle />
<RtkScreenShareToggle />
</div>
<div
id="controlbar-center"
style={{ display: "flex", alignItems: "center", justifyContent: "center" }}
>
<RtkMicToggle />
<RtkCameraToggle />
<RtkStageToggle />
<RtkLeaveButton />
<RtkMoreToggle>
<div slot="more-elements">
<RtkPipToggle variant="horizontal" />
<RtkMuteAllButton variant="horizontal" />
<RtkBreakoutRoomsToggle variant="horizontal" />
<RtkRecordingToggle variant="horizontal" />
</div>
</RtkMoreToggle>
</div>
<div
id="controlbar-right"
style={{ display: "flex", alignItems: "center", justifyContent: "center" }}
>
<RtkChatToggle />
<RtkPollsToggle />
<RtkParticipantsToggle />
<RtkPluginsToggle />
</div>
</div>为全屏目标定义一个 ref,并将其附加到你的容器元素上:
const fullScreenRef = useRef<HTMLDivElement>(null);
// In your RtkUIProvider, add the ref to the container
<RtkUIProvider
ref={fullScreenRef}
meeting={meeting}
showSetupScreen={false}
style={{
display: "flex",
flexDirection: "column",
height: "100vh",
margin: 0,
}}
>
{/* 你的控制栏和其他组件 */}
</RtkUIProvider>
// Pass the ref's current element to RtkFullscreenToggle
<RtkFullscreenToggle targetElement={fullScreenRef.current} />RealtimeKit UI Kit 提供了 rtk-controlbar 组件作为默认的控制栏。
如果需要额外的控制项,请将 rtk-controlbar 替换为单个 UI Kit 组件和自定义元素。在 构建自定义 UI 中的 renderJoinedScreen 函数中,替换:
<rtk-controlbar
style="display: flex; justify-content: space-between;"
></rtk-controlbar>为:
<div
style="display: flex; width: 100%; padding: 8px 12px; color: white; justify-content: space-between;"
>
<div
id="controlbar-left"
style="display: flex; align-items: center; justify-content: center;"
>
<rtk-fullscreen-toggle id="fullscreen-toggle"></rtk-fullscreen-toggle>
<rtk-settings-toggle></rtk-settings-toggle>
<rtk-screen-share-toggle></rtk-screen-share-toggle>
</div>
<div
id="controlbar-center"
style="display: flex; align-items: center; justify-content: center;"
>
<rtk-mic-toggle></rtk-mic-toggle>
<rtk-camera-toggle></rtk-camera-toggle>
<rtk-stage-toggle></rtk-stage-toggle>
<rtk-leave-button></rtk-leave-button>
<rtk-more-toggle>
<div slot="more-elements">
<rtk-pip-toggle variant="horizontal"></rtk-pip-toggle>
<rtk-mute-all-button variant="horizontal"></rtk-mute-all-button>
<rtk-breakout-rooms-toggle
variant="horizontal"
></rtk-breakout-rooms-toggle>
<rtk-recording-toggle variant="horizontal"></rtk-recording-toggle>
</div>
</rtk-more-toggle>
</div>
<div
id="controlbar-right"
style="display: flex; align-items: center; justify-content: center;"
>
<rtk-chat-toggle></rtk-chat-toggle>
<rtk-polls-toggle></rtk-polls-toggle>
<rtk-participants-toggle></rtk-participants-toggle>
<rtk-plugins-toggle></rtk-plugins-toggle>
</div>
</div>在渲染后注册全屏目标:
const fullscreenToggle = document.querySelector("#fullscreen-toggle");
if (fullscreenToggle) {
const targetElement = document.querySelector("rtk-ui-provider");
if (targetElement) {
fullscreenToggle.targetElement = targetElement;
}
}RealtimeKit UI Kit 提供了 rtk-controlbar 组件作为默认的控制栏。
如果需要额外的控制项,请将 rtk-controlbar 替换为单个 UI Kit 组件和自定义元素。创建一个直接使用 RealtimeKit Angular 组件的自定义控制栏组件。
import { Component, AfterViewInit, ElementRef, ViewChild } from "@angular/core";
@Component({
selector: "app-custom-controlbar",
template: `
<div class="custom-controlbar">
<div class="controlbar-left">
<rtk-fullscreen-toggle #fullscreenToggle></rtk-fullscreen-toggle>
<rtk-settings-toggle></rtk-settings-toggle>
<rtk-screen-share-toggle></rtk-screen-share-toggle>
</div>
<div class="controlbar-center">
<rtk-mic-toggle></rtk-mic-toggle>
<rtk-camera-toggle></rtk-camera-toggle>
<rtk-stage-toggle></rtk-stage-toggle>
<rtk-leave-button></rtk-leave-button>
<rtk-more-toggle>
<div slot="more-elements">
<rtk-pip-toggle variant="horizontal"></rtk-pip-toggle>
<rtk-mute-all-button variant="horizontal"></rtk-mute-all-button>
<rtk-breakout-rooms-toggle
variant="horizontal"
></rtk-breakout-rooms-toggle>
<rtk-recording-toggle variant="horizontal"></rtk-recording-toggle>
</div>
</rtk-more-toggle>
</div>
<div class="controlbar-right">
<rtk-chat-toggle></rtk-chat-toggle>
<rtk-polls-toggle></rtk-polls-toggle>
<rtk-participants-toggle></rtk-participants-toggle>
<rtk-plugins-toggle></rtk-plugins-toggle>
</div>
</div>
`,
styles: [
`
.custom-controlbar {
display: flex;
width: 100%;
padding: 8px 12px;
color: white;
justify-content: space-between;
background-color: rgba(0, 0, 0, 0.8);
border-radius: 8px;
}
.controlbar-left,
.controlbar-center,
.controlbar-right {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.controlbar-center {
flex: 1;
justify-content: center;
}
`,
],
})
export class CustomControlbarComponent implements AfterViewInit {
@ViewChild("fullscreenToggle", { static: true })
fullscreenToggle!: ElementRef;
ngAfterViewInit() {
// 渲染后注册全屏目标
this.setupFullscreenToggle();
}
private setupFullscreenToggle() {
const fullscreenElement = this.fullscreenToggle?.nativeElement;
if (fullscreenElement) {
const targetElement = document.querySelector("rtk-ui-provider");
if (targetElement) {
fullscreenElement.targetElement = targetElement;
}
}
}
}在你的主会议组件模板中,替换:
<rtk-controlbar
style="display: flex; justify-content: space-between;"
></rtk-controlbar>为:
<app-custom-controlbar></app-custom-controlbar>import {
Component,
ElementRef,
OnInit,
OnDestroy,
ViewChild,
} from "@angular/core";
@Component({
selector: "app-meeting",
template: `
<rtk-meeting #meetingComponent id="meeting-component">
<!-- Other meeting UI components -->
<rtk-grid></rtk-grid>
<rtk-sidebar></rtk-sidebar>
<!-- 自定义控制栏替换 rtk-controlbar -->
<app-custom-controlbar></app-custom-controlbar>
</rtk-meeting>
`,
})
export class MeetingComponent implements OnInit, OnDestroy {
@ViewChild("meetingComponent", { static: true }) meetingElement!: ElementRef;
meeting: any;
private authToken = "<participant_auth_token>";
async ngOnInit() {
const RealtimeKitClient = await import(
"https://cdn.jsdelivr.net/npm/@cloudflare/realtimekit@latest/dist/index.es.js"
);
this.meeting = await RealtimeKitClient.default.init({
authToken: this.authToken,
});
const meetingComponent = this.meetingElement.nativeElement;
meetingComponent.showSetupScreen = true;
meetingComponent.meeting = this.meeting;
}
ngOnDestroy() {
// 清理逻辑
}
}不要忘记在你的 Angular 模块中声明你的自定义控制栏组件:
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { AppComponent } from "./app.component";
import { MeetingComponent } from "./meeting.component";
import { CustomControlbarComponent } from "./custom-controlbar.component";
@NgModule({
declarations: [AppComponent, MeetingComponent, CustomControlbarComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA], // RTK web 组件所必需
})
export class AppModule {}你可以通过添加自己的按钮或修改布局来进一步自定义控制栏:
import { Component, AfterViewInit, ElementRef, ViewChild } from "@angular/core";
@Component({
selector: "app-enhanced-controlbar",
template: `
<div class="custom-controlbar">
<div class="controlbar-left">
<rtk-fullscreen-toggle #fullscreenToggle></rtk-fullscreen-toggle>
<rtk-settings-toggle></rtk-settings-toggle>
<button class="custom-button" (click)="onCustomAction()">
Custom Action
</button>
</div>
<div class="controlbar-center">
<rtk-mic-toggle></rtk-mic-toggle>
<rtk-camera-toggle></rtk-camera-toggle>
<rtk-stage-toggle></rtk-stage-toggle>
<rtk-leave-button></rtk-leave-button>
</div>
<div class="controlbar-right">
<rtk-chat-toggle></rtk-chat-toggle>
<rtk-participants-toggle></rtk-participants-toggle>
</div>
</div>
`,
styles: [
`
.custom-controlbar {
display: flex;
width: 100%;
padding: 8px 12px;
color: white;
justify-content: space-between;
background-color: rgba(0, 0, 0, 0.8);
border-radius: 8px;
}
.controlbar-left,
.controlbar-center,
.controlbar-right {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.custom-button {
background: #0051c3;
border: none;
color: white;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
}
.custom-button:hover {
background: #003d99;
}
`,
],
})
export class EnhancedControlbarComponent implements AfterViewInit {
@ViewChild("fullscreenToggle", { static: true })
fullscreenToggle!: ElementRef;
ngAfterViewInit() {
this.setupFullscreenToggle();
}
private setupFullscreenToggle() {
const fullscreenElement = this.fullscreenToggle?.nativeElement;
if (fullscreenElement) {
const targetElement = document.querySelector("rtk-ui-provider");
if (targetElement) {
fullscreenElement.targetElement = targetElement;
}
}
}
onCustomAction() {
console.log("Custom action triggered");
// 在此处添加你的自定义逻辑
}
}这种方法使你能够完全控制控制栏的布局,同时保持 Angular 的组件架构并利用 RealtimeKit 的内置功能。
iOS UI Kit 提供了 RtkMeetingControlBar 作为群组通话会议的默认控制栏。若要构建自定义控制栏,请在 RtkTabBar 中组合单个按钮组件。
| 组件 | 描述 |
|---|---|
RtkAudioButtonControlBar |
包含自动状态管理的麦克风切换 |
RtkVideoButtonControlBar |
包含自动状态管理的摄像头切换 |
RtkEndMeetingControlBarButton |
包含确认对话框的离开/结束会议按钮 |
RtkMoreButtonControlBar |
包含底部操作面板(bottom sheet)的“更多”菜单按钮 |
RtkSwitchCameraButtonControlBar |
前置/后置摄像头切换 |
RtkStageActionButtonControlBar |
网络研讨会和直播的舞台加入/离开 |
RtkControlBarButton |
用于自定义按钮的基类按钮 |
RtkControlBarSpacerButton |
用于布局的不可见占位符 |
创建一个 RtkTabBar 并添加单个按钮组件:
import RealtimeKitUI
import RealtimeKit
func buildCustomControlBar(
meeting: RealtimeKitClient,
viewController: UIViewController
) -> RtkTabBar {
let tabBar = RtkTabBar(delegate: nil)
let micButton = RtkAudioButtonControlBar(meeting: meeting)
let videoButton = RtkVideoButtonControlBar(rtkClient: meeting)
let switchCameraButton = RtkSwitchCameraButtonControlBar(meeting: meeting)
let endCallButton = RtkEndMeetingControlBarButton(
meeting: meeting,
alertViewController: viewController
)
tabBar.setButtons([micButton, videoButton, switchCameraButton, endCallButton])
return tabBar
}override func viewDidLoad() {
super.viewDidLoad()
let controlBar = buildCustomControlBar(
meeting: meeting,
viewController: self
)
controlBar.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(controlBar)
NSLayoutConstraint.activate([
controlBar.bottomAnchor.constraint(
equalTo: view.safeAreaLayoutGuide.bottomAnchor
),
controlBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
controlBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
controlBar.heightAnchor.constraint(equalToConstant: 80),
])
}实现 RtkMeetingControlBarDataSource 以替换特定按钮,同时保持默认控制栏布局:
class CustomControlBarDataSource: RtkMeetingControlBarDataSource {
func getMicControlBarButton(
for meeting: RealtimeKitClient
) -> RtkControlBarButton? {
// 返回自定义的麦克风按钮,或返回 nil 以使用默认按钮
let button = RtkAudioButtonControlBar(meeting: meeting)
return button
}
func getVideoControlBarButton(
for meeting: RealtimeKitClient
) -> RtkControlBarButton? {
// 返回自定义的摄像头按钮,或返回 nil 以使用默认按钮
let button = RtkVideoButtonControlBar(rtkClient: meeting)
return button
}
}
// 分配数据源
let controlBar = RtkMeetingControlBar(
meeting: meeting,
delegate: nil,
presentingViewController: self
)
controlBar.dataSource = CustomControlBarDataSource()使用 RtkControlBarButton 创建自定义按钮:
let customButton = RtkControlBarButton(
image: RtkImage(image: UIImage(systemName: "hand.raised.fill")),
title: "Raise Hand"
)
customButton.addTarget(self, action: #selector(onRaiseHand), for: .touchUpInside)
// 将其与其他按钮并列添加
tabBar.setButtons([micButton, videoButton, customButton, endCallButton])Android UI Kit 提供了 RtkMeetingControlBarView 作为默认的控制栏。若要构建自定义控制栏,请在 XML 布局中使用单个按钮组件,并使用 meeting 对象激活它们。
| 组件 | 激活媒介 | 描述 |
|---|---|---|
RtkMicToggleButton |
RealtimeKitClient |
包含状态管理的麦克风切换 |
RtkCameraToggleButton |
RealtimeKitClient |
包含状态管理的摄像头切换 |
RtkLeaveButton |
RealtimeKitClient |
包含确认对话框的离开/结束会议按钮 |
RtkMoreToggleButton |
RealtimeKitClient |
“更多”菜单按钮 |
RtkControlBarButton |
— | 用于自定义按钮的基类按钮 |
<?xml version="1.0" encoding="utf-8"?>
<com.cloudflare.realtimekit.ui.view.controlbars.RtkControlBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/customControlBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="8dp">
<com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkMicToggleButton
android:id="@+id/micToggle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="4dp" />
<com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkCameraToggleButton
android:id="@+id/cameraToggle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="4dp" />
<com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkMoreToggleButton
android:id="@+id/moreToggle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="4dp" />
<com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkLeaveButton
android:id="@+id/leaveButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="4dp" />
</com.cloudflare.realtimekit.ui.view.controlbars.RtkControlBarView>import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.cloudflare.realtimekit.RealtimeKitClient
import com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkMicToggleButton
import com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkCameraToggleButton
import com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkMoreToggleButton
import com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkLeaveButton
class CustomMeetingActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_custom_meeting)
// Your meeting initialization code...
}
fun activateControlBar(meeting: RealtimeKitClient) {
findViewById<RtkMicToggleButton>(R.id.micToggle).activate(meeting)
findViewById<RtkCameraToggleButton>(R.id.cameraToggle).activate(meeting)
findViewById<RtkMoreToggleButton>(R.id.moreToggle).activate(meeting)
findViewById<RtkLeaveButton>(R.id.leaveButton).activate(meeting)
}
}在 XML 中创建自定义按钮:
<com.cloudflare.realtimekit.ui.view.controlbarbuttons.RtkControlBarButton
android:id="@+id/raiseHandButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="4dp"
android:text="Raise Hand"
app:rtk_cbb_icon="@drawable/ic_raise_hand"
app:rtk_cbb_variant="BUTTON"
app:rtk_cbb_showText="true" />在 Kotlin 中处理点击事件:
val raiseHandButton = findViewById<RtkControlBarButton>(R.id.raiseHandButton)
raiseHandButton.setOnClickListener {
// 自定义举手逻辑
}Flutter UI Kit 提供了单个切换按钮挂件,你可以使用标准 Flutter 布局挂件将其组合成自定义控制栏。
| 挂件 | 必填参数 | 描述 |
|---|---|---|
RtkSelfAudioToggleButton |
RealtimekitClient |
包含权限处理的麦克风切换 |
RtkSelfVideoToggleButton |
RealtimekitClient |
包含权限处理的摄像头切换 |
RtkLeaveButton |
RealtimekitClient |
包含确认对话框的离开会议按钮 |
将按钮挂件包裹在 Row 或任何自定义布局中:
import 'package:flutter/material.dart';
import 'package:realtimekit_ui/realtimekit_ui.dart';
class CustomControlBar extends StatelessWidget {
final RealtimekitClient meeting;
const CustomControlBar({required this.meeting, super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RtkSelfAudioToggleButton(meeting: meeting),
RtkSelfVideoToggleButton(meeting: meeting),
RtkLeaveButton(meeting: meeting),
],
),
);
}
}替换你 Scaffold 中的默认控制栏:
import 'package:flutter/material.dart';
import 'package:realtimekit_ui/realtimekit_ui.dart';
class CustomMeetingScreen extends StatelessWidget {
final RealtimekitClient meeting;
const CustomMeetingScreen({required this.meeting, super.key});
@override
Widget build(BuildContext context) {
return RtkProvider(
meeting: meeting,
uiKitInfo: uiKitInfo,
child: Scaffold(
backgroundColor: Colors.black,
body: Column(
children: [
RtkMeetingTitle(meeting: meeting),
// 你的参与者网格放置在此处
const Spacer(),
],
),
bottomNavigationBar: CustomControlBar(meeting: meeting),
),
);
}
}在 RealtimeKit 按钮旁边添加你自己的 IconButton 或任何 Flutter 挂件:
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RtkSelfAudioToggleButton(meeting: meeting),
RtkSelfVideoToggleButton(meeting: meeting),
// 自定义举手按钮
IconButton(
onPressed: () {
// 自定义举手逻辑
},
icon: const Icon(Icons.back_hand, color: Colors.white),
),
RtkLeaveButton(meeting: meeting),
],
)传入 individualDesignToken 以单独设置按钮的样式:
RtkSelfAudioToggleButton(
meeting: meeting,
iconSize: 28.0,
iconColor: Colors.cyan,
showLabel: true,
)React Native UI Kit 提供了单个切换组件,你可以将其组合成自定义控制栏。
| 组件 | 必填 prop | 描述 |
|---|---|---|
RtkMicToggle |
meeting |
包含权限处理的麦克风切换 |
RtkCameraToggle |
meeting |
包含权限处理的摄像头切换 |
RtkLeaveButton |
— | 包含确认对话框的离开/结束会议按钮 |
RtkMoreToggle |
meeting |
包含通知徽标的“更多”菜单切换 |
RtkScreenShareToggle |
meeting |
屏幕共享切换 |
RtkControlbarButton |
— | 用于自定义操作的基准按钮 |
在 View 中将 RtkControlbar 替换为单个组件:
import React from "react";
import { View, StyleSheet } from "react-native";
import {
RtkMicToggle,
RtkCameraToggle,
RtkLeaveButton,
RtkMoreToggle,
RtkScreenShareToggle,
} from "@cloudflare/realtimekit-react-native-ui";
function CustomControlbar({ meeting }) {
return (
<View style={styles.controlbar}>
<RtkMicToggle meeting={meeting} variant="horizontal" />
<RtkCameraToggle meeting={meeting} variant="horizontal" />
<RtkScreenShareToggle meeting={meeting} variant="horizontal" />
<RtkMoreToggle meeting={meeting} variant="horizontal" />
<RtkLeaveButton />
</View>
);
}
const styles = StyleSheet.create({
controlbar: {
flexDirection: "row",
justifyContent: "space-evenly",
alignItems: "center",
backgroundColor: "#1A1A1A",
paddingVertical: 8,
paddingHorizontal: 16,
},
});在 构建自定义 UI 中的 MeetingScreens 组件里,替换:
<RtkControlbar meeting={meeting} />为:
<CustomControlbar meeting={meeting} />使用 RtkControlbarButton 创建符合 RealtimeKit 设计的按钮:
import { RtkControlbarButton } from "@cloudflare/realtimekit-react-native-ui";
function CustomControlbar({ meeting }) {
return (
<View style={styles.controlbar}>
<RtkMicToggle meeting={meeting} variant="horizontal" />
<RtkCameraToggle meeting={meeting} variant="horizontal" />
<RtkControlbarButton
label="Raise Hand"
icon={raiseHandSvg}
onClick={() => {
// 自定义举手逻辑
}}
variant="horizontal"
/>
<RtkLeaveButton />
</View>
);
}