Improve example for better feature testing (#21)

* fixed

* format

* set track to null

* rearrange

* toggle subscribed

* clean

* `RemoteTrackPublicationMenuWidget` for mute/unmute subscribe/unsubscribe
This commit is contained in:
Hiroshi Horie
2021-10-21 21:11:25 +09:00
committed by GitHub
parent 8cd8edb617
commit 6bba6b4006
3 changed files with 103 additions and 29 deletions
+1 -7
View File
@@ -71,13 +71,7 @@ class _RoomPageState extends State<RoomPage> {
// Create video track // Create video track
final localVideo = await LocalVideoTrack.createCameraTrack(); final localVideo = await LocalVideoTrack.createCameraTrack();
// Try to publish the video // Try to publish the video
await widget.room.localParticipant.publishVideoTrack( await widget.room.localParticipant.publishVideoTrack(localVideo);
localVideo,
// options: TrackPublishOptions(
// // simulcast: true,
// videoEncoding: VideoParameters.presetQVGA169.encoding,
// ),
);
// Create mic track // Create mic track
final localAudio = await LocalAudioTrack.create(); final localAudio = await LocalAudioTrack.create();
+98 -18
View File
@@ -1,4 +1,5 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:livekit_client/livekit_client.dart'; import 'package:livekit_client/livekit_client.dart';
@@ -23,8 +24,8 @@ class ParticipantWidget extends StatefulWidget {
class _ParticipantWidgetState extends State<ParticipantWidget> { class _ParticipantWidgetState extends State<ParticipantWidget> {
// //
TrackPublication? videoPub; TrackPublication? firstVideoPub;
TrackPublication? audioPub; TrackPublication? firstAudioPub;
@override @override
void initState() { void initState() {
@@ -50,18 +51,14 @@ class _ParticipantWidgetState extends State<ParticipantWidget> {
// register for change so Flutter will re-build the widget upon change // register for change so Flutter will re-build the widget upon change
void _onParticipantChanged() { void _onParticipantChanged() {
// //
final firstAudio = widget.participant.audioTracks
.firstWhereOrNull((pub) => pub.subscribed);
final firstVideo = widget.participant.videoTracks
.firstWhereOrNull((pub) => !pub.isScreenShare && pub.subscribed);
if (firstVideo is RemoteTrackPublication) {
firstVideo.videoQuality = widget.quality;
}
setState(() { setState(() {
audioPub = !(firstAudio?.muted ?? true) ? firstAudio : null; // For simplification, We are assuming here
videoPub = !(firstVideo?.muted ?? true) ? firstVideo : null; // there is only 1 video / audio tracks.
firstAudioPub = widget.participant.audioTracks.firstOrNull;
firstVideoPub = widget.participant.videoTracks.firstOrNull;
if (firstVideoPub is RemoteTrackPublication) {
(firstVideoPub as RemoteTrackPublication).videoQuality = widget.quality;
}
}); });
} }
@@ -71,9 +68,10 @@ class _ParticipantWidgetState extends State<ParticipantWidget> {
child: Stack( child: Stack(
children: [ children: [
// Video // Video
if (videoPub != null) if (firstVideoPub?.subscribed == true &&
firstVideoPub?.muted == false)
VideoTrackRenderer( VideoTrackRenderer(
videoPub!.track as VideoTrack, firstVideoPub!.track as VideoTrack,
fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
) )
else else
@@ -81,12 +79,94 @@ class _ParticipantWidgetState extends State<ParticipantWidget> {
Align( Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: ParticipantInfoWidget( child: Column(
title: widget.participant.identity, crossAxisAlignment: CrossAxisAlignment.stretch,
muted: audioPub == null, mainAxisSize: MainAxisSize.min,
children: [
//
// Menu for Video RemoteTrackPublication
//
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (firstVideoPub is RemoteTrackPublication)
RemoteTrackPublicationMenuWidget(
pub: firstVideoPub as RemoteTrackPublication,
icon: EvaIcons.videoOutline,
),
//
// Menu for Audio RemoteTrackPublication
//
if (firstAudioPub is RemoteTrackPublication)
RemoteTrackPublicationMenuWidget(
pub: firstAudioPub as RemoteTrackPublication,
icon: EvaIcons.volumeUpOutline,
),
],
),
ParticipantInfoWidget(
title: widget.participant.identity,
audioAvailable: firstAudioPub?.muted == false &&
firstAudioPub?.subscribed == true,
),
],
), ),
), ),
], ],
), ),
); );
} }
class RemoteTrackPublicationMenuWidget extends StatelessWidget {
final IconData icon;
final RemoteTrackPublication pub;
const RemoteTrackPublicationMenuWidget({
required this.pub,
required this.icon,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) => Material(
// type: MaterialType.card,
color: Colors.black.withOpacity(0.3),
// shape: CircleBorder(),
child: PopupMenuButton<Function>(
// shape: CircleBorder(),
icon: Icon(icon),
onSelected: (value) => value(),
itemBuilder: (BuildContext context) {
return <PopupMenuEntry<Function>>[
//
// Mute/Unmute
//
if (pub.muted == false)
PopupMenuItem(
child: const Text('Mute'),
value: () => pub.muted = true,
),
if (pub.muted == true)
PopupMenuItem(
child: const Text('Un-mute'),
value: () => pub.muted = false,
),
//
// Subscribe/Unsubscribe
//
if (pub.subscribed == false)
PopupMenuItem(
child: const Text('Subscribe'),
value: () => pub.subscribed = true,
),
if (pub.subscribed == true)
PopupMenuItem(
child: const Text('Un-subscribe'),
value: () => pub.subscribed = false,
),
];
},
),
);
}
+4 -4
View File
@@ -4,11 +4,11 @@ import 'package:flutter/material.dart';
class ParticipantInfoWidget extends StatelessWidget { class ParticipantInfoWidget extends StatelessWidget {
// //
final String? title; final String? title;
final bool muted; final bool audioAvailable;
const ParticipantInfoWidget({ const ParticipantInfoWidget({
this.title, this.title,
this.muted = true, this.audioAvailable = true,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@@ -33,8 +33,8 @@ class ParticipantInfoWidget extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.only(left: 5), padding: const EdgeInsets.only(left: 5),
child: Icon( child: Icon(
!muted ? EvaIcons.mic : EvaIcons.micOff, audioAvailable ? EvaIcons.mic : EvaIcons.micOff,
color: !muted ? Colors.white : Colors.red, color: audioAvailable ? Colors.white : Colors.red,
size: 16, size: 16,
), ),
), ),