Skip to content

Commit 6bba6b4

Browse files
authored
Improve example for better feature testing (#21)
* fixed * format * set track to null * rearrange * toggle subscribed * clean * `RemoteTrackPublicationMenuWidget` for mute/unmute subscribe/unsubscribe
1 parent 8cd8edb commit 6bba6b4

File tree

3 files changed

+103
-29
lines changed

3 files changed

+103
-29
lines changed

example/lib/pages/room.dart

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,7 @@ class _RoomPageState extends State<RoomPage> {
7171
// Create video track
7272
final localVideo = await LocalVideoTrack.createCameraTrack();
7373
// Try to publish the video
74-
await widget.room.localParticipant.publishVideoTrack(
75-
localVideo,
76-
// options: TrackPublishOptions(
77-
// // simulcast: true,
78-
// videoEncoding: VideoParameters.presetQVGA169.encoding,
79-
// ),
80-
);
74+
await widget.room.localParticipant.publishVideoTrack(localVideo);
8175

8276
// Create mic track
8377
final localAudio = await LocalAudioTrack.create();

example/lib/widgets/participant.dart

Lines changed: 98 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:collection/collection.dart';
2+
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
23
import 'package:flutter/material.dart';
34
import 'package:flutter_webrtc/flutter_webrtc.dart';
45
import 'package:livekit_client/livekit_client.dart';
@@ -23,8 +24,8 @@ class ParticipantWidget extends StatefulWidget {
2324

2425
class _ParticipantWidgetState extends State<ParticipantWidget> {
2526
//
26-
TrackPublication? videoPub;
27-
TrackPublication? audioPub;
27+
TrackPublication? firstVideoPub;
28+
TrackPublication? firstAudioPub;
2829

2930
@override
3031
void initState() {
@@ -50,18 +51,14 @@ class _ParticipantWidgetState extends State<ParticipantWidget> {
5051
// register for change so Flutter will re-build the widget upon change
5152
void _onParticipantChanged() {
5253
//
53-
final firstAudio = widget.participant.audioTracks
54-
.firstWhereOrNull((pub) => pub.subscribed);
55-
final firstVideo = widget.participant.videoTracks
56-
.firstWhereOrNull((pub) => !pub.isScreenShare && pub.subscribed);
57-
58-
if (firstVideo is RemoteTrackPublication) {
59-
firstVideo.videoQuality = widget.quality;
60-
}
61-
6254
setState(() {
63-
audioPub = !(firstAudio?.muted ?? true) ? firstAudio : null;
64-
videoPub = !(firstVideo?.muted ?? true) ? firstVideo : null;
55+
// For simplification, We are assuming here
56+
// there is only 1 video / audio tracks.
57+
firstAudioPub = widget.participant.audioTracks.firstOrNull;
58+
firstVideoPub = widget.participant.videoTracks.firstOrNull;
59+
if (firstVideoPub is RemoteTrackPublication) {
60+
(firstVideoPub as RemoteTrackPublication).videoQuality = widget.quality;
61+
}
6562
});
6663
}
6764

@@ -71,22 +68,105 @@ class _ParticipantWidgetState extends State<ParticipantWidget> {
7168
child: Stack(
7269
children: [
7370
// Video
74-
if (videoPub != null)
71+
if (firstVideoPub?.subscribed == true &&
72+
firstVideoPub?.muted == false)
7573
VideoTrackRenderer(
76-
videoPub!.track as VideoTrack,
74+
firstVideoPub!.track as VideoTrack,
7775
fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
7876
)
7977
else
8078
const NoVideoWidget(),
8179

8280
Align(
8381
alignment: Alignment.bottomCenter,
84-
child: ParticipantInfoWidget(
85-
title: widget.participant.identity,
86-
muted: audioPub == null,
82+
child: Column(
83+
crossAxisAlignment: CrossAxisAlignment.stretch,
84+
mainAxisSize: MainAxisSize.min,
85+
children: [
86+
//
87+
// Menu for Video RemoteTrackPublication
88+
//
89+
Row(
90+
mainAxisSize: MainAxisSize.max,
91+
mainAxisAlignment: MainAxisAlignment.end,
92+
children: [
93+
if (firstVideoPub is RemoteTrackPublication)
94+
RemoteTrackPublicationMenuWidget(
95+
pub: firstVideoPub as RemoteTrackPublication,
96+
icon: EvaIcons.videoOutline,
97+
),
98+
//
99+
// Menu for Audio RemoteTrackPublication
100+
//
101+
if (firstAudioPub is RemoteTrackPublication)
102+
RemoteTrackPublicationMenuWidget(
103+
pub: firstAudioPub as RemoteTrackPublication,
104+
icon: EvaIcons.volumeUpOutline,
105+
),
106+
],
107+
),
108+
109+
ParticipantInfoWidget(
110+
title: widget.participant.identity,
111+
audioAvailable: firstAudioPub?.muted == false &&
112+
firstAudioPub?.subscribed == true,
113+
),
114+
],
87115
),
88116
),
89117
],
90118
),
91119
);
92120
}
121+
122+
class RemoteTrackPublicationMenuWidget extends StatelessWidget {
123+
final IconData icon;
124+
final RemoteTrackPublication pub;
125+
const RemoteTrackPublicationMenuWidget({
126+
required this.pub,
127+
required this.icon,
128+
Key? key,
129+
}) : super(key: key);
130+
131+
@override
132+
Widget build(BuildContext context) => Material(
133+
// type: MaterialType.card,
134+
color: Colors.black.withOpacity(0.3),
135+
// shape: CircleBorder(),
136+
child: PopupMenuButton<Function>(
137+
// shape: CircleBorder(),
138+
icon: Icon(icon),
139+
onSelected: (value) => value(),
140+
itemBuilder: (BuildContext context) {
141+
return <PopupMenuEntry<Function>>[
142+
//
143+
// Mute/Unmute
144+
//
145+
if (pub.muted == false)
146+
PopupMenuItem(
147+
child: const Text('Mute'),
148+
value: () => pub.muted = true,
149+
),
150+
if (pub.muted == true)
151+
PopupMenuItem(
152+
child: const Text('Un-mute'),
153+
value: () => pub.muted = false,
154+
),
155+
//
156+
// Subscribe/Unsubscribe
157+
//
158+
if (pub.subscribed == false)
159+
PopupMenuItem(
160+
child: const Text('Subscribe'),
161+
value: () => pub.subscribed = true,
162+
),
163+
if (pub.subscribed == true)
164+
PopupMenuItem(
165+
child: const Text('Un-subscribe'),
166+
value: () => pub.subscribed = false,
167+
),
168+
];
169+
},
170+
),
171+
);
172+
}

example/lib/widgets/participant_info.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import 'package:flutter/material.dart';
44
class ParticipantInfoWidget extends StatelessWidget {
55
//
66
final String? title;
7-
final bool muted;
7+
final bool audioAvailable;
88

99
const ParticipantInfoWidget({
1010
this.title,
11-
this.muted = true,
11+
this.audioAvailable = true,
1212
Key? key,
1313
}) : super(key: key);
1414

@@ -33,8 +33,8 @@ class ParticipantInfoWidget extends StatelessWidget {
3333
Padding(
3434
padding: const EdgeInsets.only(left: 5),
3535
child: Icon(
36-
!muted ? EvaIcons.mic : EvaIcons.micOff,
37-
color: !muted ? Colors.white : Colors.red,
36+
audioAvailable ? EvaIcons.mic : EvaIcons.micOff,
37+
color: audioAvailable ? Colors.white : Colors.red,
3838
size: 16,
3939
),
4040
),

0 commit comments

Comments
 (0)