很多人对 Instagram 和 Facebook 的直播功能十分好奇,如果你是一个 flutter 开发者,或打算用 Flutter 进行开发,可以使用声网实时通信 SDK 在 flutter 中添加实时聊天功能。声网是一个 RTC SDK,为 Android、iOS、Web、Windows、macOS、Unity、 electron和 flutter 等平台提供 SDK,类型包括视频 SDK、语音 SDK、交互式游戏 SDK、实时消息 SDK、云录制等。
我们会在这个项目中使用声网视频 SDK 和实时消息 SDK。
我们开始吧!
首先,使用下列两种方法创建一个新的 Flutter 项目:
- Command Line
- Android Studio
我在本项目中使用 Android Studio。因此,先创建一个新项目(点击这里查看创建 flutter 项目的教程),然后根据个人喜好为该项目命名。
现在,清理 main.dart 文件,添加一个带 Logo 的启动画面。因为我们使用的是声网 SDK,创建的软件与 Instagram 类似,所以我将其命名为 Agoragram。
启动画面
先从启动画面入手,点击这里获取代码。设计如下所示:
定时器结束时,我把路由改为 HomeScreen,之后我会将 HomeScreen 作为基本路由。
启动页面搭建完成之后,我们需要构建一个登录页面。我将 Firebase 身份验证用作登录和注册页面的后端,因此要先创建一个 Firebase 项目,将其与 flutter 链接,大家可以点击这里查看引用。
将 Firebase 集成到 Flutter 项目后,我们要构建 login.dart 文件。下文会提到用于登录的微件设计:
- 一段文本;
- 用来分别输入电子邮件和密码的两个文本域。
注意,我使用了一些在 dart 文件中定义的函数,因为我已在另一个文件中提出了所有 Firebase 请求,具体细节我会分享在文末,大家可以点击这里查看登录代码。
登录后,创建一个注册微件。注册需要头像、用户名、姓名、电子邮件和密码。另外,我们需要创建一个输入值无效提示,在用户的输入值无效时弹出提示(点击这里查看代码)。
我在上方的页面下方添加了一个页脚,单击该页脚可以跳转到登录页面。我们需要一个可以显示人相的头像,另外,用户名必须具有唯一性。
完成登录或注册后,我将名称、电子邮件、头像等数据保存在 Firebase 服务器和共享的“Preference”中(以便随时获取详细信息)。
主界面(Home Screen)
我们要构建的下一个页面是主界面,我添加了 2 个包含图像和评论的帖子,都是 app 里自带的(本教程不讨论这部分内容),帖子前面有用户的头像和姓名,我们可以在图片下方添加心形、评论、分享和保存等图标,我们还可以为图像添加描述。以上是计划构建的基本布局。
这是主界面中单个帖子的布局。为便于管理 UI,我为帖子使用了一个类函数。
因为我们要构建的是直播推流,所以需要两个 UI,分别用来主持直播和观看直播,如下所示:
上图的“test”是我们用户名,“y”是另一个用户的用户名,为了使设计如“y”所示,我们需要堆叠一些微件。我的想法是,只要用户主持一场现场直播,就在 Firebase 中创建一个文档,并显示在此处,所以我们需要一个滚动视图,用来横向管理直播用户。为便于轻松管理数据,我也为直播用户使用了类函数。点击这里查看构建主界面的代码。完成后的主界面如下所示:
然后,开始使用直播功能。我们希望发起直播的人(我们称此类用户为主播用户)可以向其他用户分享摄像头视图和声音。观看直播的其他人只能观看,不能共享摄像头和音频。主机有添加观众的功能,但我们先不构建这个功能。
我用声网 SDK 来构建视频共享平台,其他 RTC SDK 比起来,声网 SDK 还是非常好用的。我构建这个 app 使用的是 GitHub 库的引用,欢迎大家点击查看,请按照声网平台的步骤进行操作:
第一步:创建一个帐户
1、在 www.agora.io 上创建一个开发者帐户;
2、单击右上角的注册按钮;
3、填写注册信息,创建帐户;
4、创建完帐户后,登录。
第二步:开发者控制面板
1、注册完成后就可以进入开发者面板,这里你可以访问所有开发者资源;
2、点击左上角的“Projects”选项卡,能看到一个创建好的默认项目;
3、找到 App ID,它是你使用声网服务的 API 密钥。
有了这个 App ID,我们才能在项目中运行声网 SDK。
接下来要添加依赖项,请打开 pubspec.yaml,输入以下内容:
agora_rtc_engine: 1.0.5 # for video
agora_rtm: 0.9.11 # for chatting
你可以在存储 App ID 的我的项目中找到 setting.dart 文件,将 App ID 粘贴在该文件中。
const APP_ID = ""; // Enter the App ID in between the double quotes
获取项目的所有依赖项:
flutter pub get
直播的预期 UI 如下图所示:
图一
图二
我们需要使用声网引擎来初始化视频和音频,对于评论,我们使用的是声网 RTM。 我们有查看加入直播场景中的直播用户的功能,还可以静音、切换摄像头,还可以点击按钮结束直播,布局如上图二。
可以点击这里查看完整的 dart 文件,我会说明使用的内容和原因。
Future<void> initialize() async {
await _initAgoraRtcEngine();
_addAgoraEventHandlers();
await AgoraRtcEngine.enableWebSdkInteroperability(true);
await AgoraRtcEngine.setParameters(
'''{\"che.video.lowBitRateStreamParameter\":{\"width\":320,\"height\":180,\"frameRate\":15,\"bitRate\":140}}''');
await AgoraRtcEngine.joinChannel(null, widget.channelName, null, 0);
}
/// Create agora sdk instance and initialize
Future<void> _initAgoraRtcEngine() async {
await AgoraRtcEngine.create(APP_ID);
await AgoraRtcEngine.enableVideo();
}
/// Add agora event handlers
void _addAgoraEventHandlers() {
AgoraRtcEngine.onJoinChannelSuccess = (
String channel,
int uid,
int elapsed,
) async{
final documentId = widget.channelName;
channelName= documentId;
FireStoreClass.createLiveUser(name: documentId,id: uid,time: widget.time,image:widget.image);
// The above line create a document in the firestore with username as documentID
await Wakelock.enable();
// This is used for Keeping the device awake. Its now enabled
};
AgoraRtcEngine.onLeaveChannel = () {
setState(() {
_users.clear();
});
};
AgoraRtcEngine.onUserJoined = (int uid, int elapsed) {
setState(() {
_users.add(uid);
});
};
AgoraRtcEngine.onUserOffline = (int uid, int reason) {
setState(() {
_users.remove(uid);
});
};
}
我们在 initState() 内部调用 initialize 函数,用来初始化声网引擎。我还使用了 Wakelock 软件包,让设备保持唤醒状态,否则设备会在特定时间后自动锁定,不过要记得在直播结束后禁用 Wakelock。
@override
void initState() {
super.initState();
// initialize agora sdk
initialize(); // Initialise agora engine
_createClient(); // initialise agora RTM
}
如果要为声网 RTM 创建客户端,我们还需要在 initState() 中添加该函数。
另外,我们还需获取 Rendered Widget 来显示画面,可以使用下列命令:
AgoraRenderWidget(0, local: true, preview: true),
目前 Layout 已经设置完成。我们只需要主机用户的屏幕,启动视频频道需要 channelName,channelName 也应具有唯一性,避免造成交叉混淆。因为我们已经将用户名设为唯一,所以直接用用户名作为频道名称。
接下来,我们用声网 RTM 搭建评论功能。在主机文件的 initState() 中调用 createClient() 函数,此函数定义了一个用户发送消息的 RTM 通道。
void _createClient() async {
_client =
await AgoraRtmClient.createInstance('<Your App ID Here>');
_client.onMessageReceived = (AgoraRtmMessage message, String peerId) {
_log(user: peerId, info: message.text, type: 'message');
};
_client.onConnectionStateChanged = (int state, int reason) {
if (state == 5) {
_client.logout();
setState(() {
_isLogin = false;
});
}
};
await _client.login(null, widget.channelName );
_channel = await _createChannel(widget.channelName);
await _channel.join();
}
不要忘记在 AgoraRtmClient.createInstance() 中添加你的 App ID。
我用 channel.getMembers().length 的 RTM 功能查看加入直播的用户数量,此功能可以返回加入频道的用户数量,代码如下:
Future<AgoraRtmChannel> _createChannel(String name) async {
AgoraRtmChannel channel = await _client.createChannel(name);
channel.onMemberJoined = (AgoraRtmMember member) async {
var img = await FireStoreClass.getImage(username: member.userId);
userMap.putIfAbsent(member.userId, () => img);
var len;
_channel.getMembers().then((value) {
len = value.length;
setState(() {
userNo= len-1 ;
});
});
_log(info: 'Member joined: ', user: member.userId,type: 'join');
};
channel.onMemberLeft = (AgoraRtmMember member) {
var len;
_channel.getMembers().then((value) {
len = value.length;
setState(() {
userNo= len-1 ;
});
});
};
channel.onMessageReceived =
(AgoraRtmMessage message, AgoraRtmMember member) {
_log(user: member.userId, info: message.text,type: 'message');
};
return channel;
}
void _log({String info,String type,String user}) {
var image = userMap[user];
Message m = new Message(
message: info, type: type, user: user, image: image);
setState(() {
_infoStrings.insert(0, m);
});
}
如果有用户加入频道,我会获取用户的头像并将其添加到地图中,这样就只用从服务器中获取一次图像,无需在用户评论时多次调用 image 函数。
每当有用户加入频道,聊天框中都会显示一条消息,表示有用户加入频道。 每当有用户发表评论,就会显示一条带有用户图像、用户名的消息。
以上是我认为的主持人频道要使用的主要功能,大家可以点击这里查看完整设计。
下一步是用户加入频道观看实时直播,因此我们要对主持人之外的用户的界面做一些更改。用户不需要使用麦克风和摄像头图标,下图就是用户加入直播后的界面显示:
屏幕的左上角显示主机用户的头像,右上角显示用户数,下方有 comment 和 heart 图标。我不太会制作 heart 动画,只能制作一个与其相似度高达 90% 的动画。
一起来看看我们需要做的更改,我们要在 _initAgoraRtcEngine() 中静音用户音频并关闭本地视频。
Future<void> _initAgoraRtcEngine() async {
await AgoraRtcEngine.create(APP_ID);
await AgoraRtcEngine.enableVideo();
await AgoraRtcEngine.muteLocalAudioStream(muted); // Changes
await AgoraRtcEngine.enableLocalVideo(!muted); // Changes
}
对于 heart 动画,我正在使用一个带数学运算的自定义 dart 文件,可以点击这里查看自定义的 dart 文件。 在 join.dart 中调用 HeartAnim,我用的是下列代码:
Widget heartPop(){
final size = MediaQuery.of(context).size;
final confetti = <Widget>[];
for (var i = 0; i < _numConfetti; i++) {
final height = _random.nextInt(size.height.floor());
final width = 20;
confetti.add(HeartAnim(height % 200.0,
width.toDouble(),1));
}
return Container(
child: Padding(
padding: const EdgeInsets.only(bottom:20),
child: Align(
alignment: Alignment.bottomRight,
child: Container(
height: 400,
width: 200,
child: Stack(
children: confetti,
),
),
),
),
);
}
这样可以一次性制作出 10 个大小和图形不同的心形图标,但因为要以动画形式显示心形图标,所以要让心形图标循环 4 秒钟:
void popUp() async{
setState(() {
heart=true;
});
Timer(Duration(seconds: 4), () =>
{
_timer.cancel(),
setState(() {
heart=false;
})
});
_timer = Timer.periodic(Duration(milliseconds: 125), (Timer t) {
setState(() {
_height += _random.nextInt(20);
});
});
}
上面的代码将在剩余的 4 秒钟内调用 heartPop。
以上就是加入页面的所有更改,点击这里查看代码。
总结
以上就是本文的所有内容。我说明了所有需要注意的代码,剩余的代码要么是微件,要么不言自明。希望这篇文章可以帮到大家。
大家可以点击这里查看 GitHub 仓库,可以试着创建应用或 Fork 这个仓库。如果大家有更好的方法或发现了文章中的任何错误,欢迎随时联系我。
原文作者:Akarsh Ashok
原文链接:https://medium.com/@akarshashok12/live-broadcast-using-agora-sdk-5befba0fbbed