update example (no casting)
This commit is contained in:
@@ -133,7 +133,7 @@ class _RoomPageState extends State<RoomPage> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: participants.isNotEmpty
|
||||
? ParticipantWidget(participants.first)
|
||||
? ParticipantWidget.widgetFor(participants.first)
|
||||
: Container()),
|
||||
SizedBox(
|
||||
height: 100,
|
||||
@@ -143,8 +143,7 @@ class _RoomPageState extends State<RoomPage> {
|
||||
itemBuilder: (BuildContext context, int index) => SizedBox(
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: ParticipantWidget(participants[index + 1],
|
||||
quality: VideoQuality.LOW),
|
||||
child: ParticipantWidget.widgetFor(participants[index + 1]),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -8,26 +8,60 @@ import 'package:livekit_example/theme.dart';
|
||||
import 'no_video.dart';
|
||||
import 'participant_info.dart';
|
||||
|
||||
class ParticipantWidget extends StatefulWidget {
|
||||
//
|
||||
final Participant participant;
|
||||
abstract class ParticipantWidget extends StatefulWidget {
|
||||
// Convenience method to return relevant widget for participant
|
||||
static ParticipantWidget widgetFor(Participant participant) {
|
||||
if (participant is LocalParticipant) {
|
||||
return LocalParticipantWidget(participant);
|
||||
} else if (participant is RemoteParticipant) {
|
||||
return RemoteParticipantWidget(participant);
|
||||
}
|
||||
throw UnimplementedError('Unknown participant type');
|
||||
}
|
||||
|
||||
// Must be implemented by child class
|
||||
abstract final Participant participant;
|
||||
final VideoQuality quality;
|
||||
|
||||
const ParticipantWidget(
|
||||
this.participant, {
|
||||
const ParticipantWidget({
|
||||
this.quality = VideoQuality.MEDIUM,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
}
|
||||
|
||||
class LocalParticipantWidget extends ParticipantWidget {
|
||||
@override
|
||||
final LocalParticipant participant;
|
||||
|
||||
const LocalParticipantWidget(
|
||||
this.participant, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ParticipantWidgetState();
|
||||
State<StatefulWidget> createState() => _LocalParticipantWidgetState();
|
||||
}
|
||||
|
||||
class _ParticipantWidgetState extends State<ParticipantWidget> {
|
||||
class RemoteParticipantWidget extends ParticipantWidget {
|
||||
@override
|
||||
final RemoteParticipant participant;
|
||||
|
||||
const RemoteParticipantWidget(
|
||||
this.participant, {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _RemoteParticipantWidgetState();
|
||||
}
|
||||
|
||||
abstract class _ParticipantWidgetState<T extends ParticipantWidget>
|
||||
extends State<T> {
|
||||
//
|
||||
TrackPublication? firstVideoPub;
|
||||
TrackPublication? firstAudioPub;
|
||||
bool _visible = true;
|
||||
VideoTrack? get activeVideoTrack;
|
||||
TrackPublication? get firstVideoPublication;
|
||||
TrackPublication? get firstAudioPublication;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -43,7 +77,7 @@ class _ParticipantWidgetState extends State<ParticipantWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant ParticipantWidget oldWidget) {
|
||||
void didUpdateWidget(covariant T oldWidget) {
|
||||
oldWidget.participant.removeListener(_onParticipantChanged);
|
||||
widget.participant.addListener(_onParticipantChanged);
|
||||
_onParticipantChanged();
|
||||
@@ -56,14 +90,17 @@ class _ParticipantWidgetState extends State<ParticipantWidget> {
|
||||
setState(() {
|
||||
// For simplification, We are assuming here
|
||||
// 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;
|
||||
}
|
||||
// firstAudioPub = widget.participant.audioTracks.firstOrNull;
|
||||
// firstVideoPub = widget.participant.videoTracks.firstOrNull;
|
||||
// if (firstVideoPub is RemoteTrackPublication) {
|
||||
// (firstVideoPub as RemoteTrackPublication).videoQuality = widget.quality;
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
// Widgets to stack above
|
||||
List<Widget> above() => [];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext ctx) => Container(
|
||||
foregroundDecoration: BoxDecoration(
|
||||
@@ -82,49 +119,26 @@ class _ParticipantWidgetState extends State<ParticipantWidget> {
|
||||
// Video
|
||||
InkWell(
|
||||
onTap: () => setState(() => _visible = !_visible),
|
||||
child: (firstVideoPub?.subscribed == true &&
|
||||
firstVideoPub?.muted == false &&
|
||||
_visible)
|
||||
child: activeVideoTrack != null
|
||||
? VideoTrackRenderer(
|
||||
firstVideoPub!.track as VideoTrack,
|
||||
activeVideoTrack!,
|
||||
fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
|
||||
)
|
||||
: const NoVideoWidget(),
|
||||
),
|
||||
|
||||
// Bottom bar
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
...above(),
|
||||
ParticipantInfoWidget(
|
||||
title: widget.participant.identity,
|
||||
audioAvailable: firstAudioPub?.muted == false &&
|
||||
firstAudioPub?.subscribed == true,
|
||||
audioAvailable: firstAudioPublication?.muted == false &&
|
||||
firstAudioPublication?.subscribed == true,
|
||||
connectionQuality: widget.participant.connectionQuality,
|
||||
),
|
||||
],
|
||||
@@ -135,6 +149,67 @@ class _ParticipantWidgetState extends State<ParticipantWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
class _LocalParticipantWidgetState
|
||||
extends _ParticipantWidgetState<LocalParticipantWidget> {
|
||||
@override
|
||||
LocalTrackPublication<LocalVideoTrack>? get firstVideoPublication =>
|
||||
widget.participant.videoTracks.firstOrNull;
|
||||
|
||||
@override
|
||||
LocalTrackPublication<LocalAudioTrack>? get firstAudioPublication =>
|
||||
widget.participant.audioTracks.firstOrNull;
|
||||
|
||||
@override
|
||||
VideoTrack? get activeVideoTrack {
|
||||
if (firstVideoPublication?.subscribed == true &&
|
||||
firstVideoPublication?.muted == false &&
|
||||
_visible) {
|
||||
return firstVideoPublication?.track;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _RemoteParticipantWidgetState
|
||||
extends _ParticipantWidgetState<RemoteParticipantWidget> {
|
||||
@override
|
||||
RemoteTrackPublication<RemoteVideoTrack>? get firstVideoPublication =>
|
||||
widget.participant.videoTracks.firstOrNull;
|
||||
|
||||
@override
|
||||
RemoteTrackPublication<RemoteAudioTrack>? get firstAudioPublication =>
|
||||
widget.participant.audioTracks.firstOrNull;
|
||||
|
||||
@override
|
||||
VideoTrack? get activeVideoTrack {
|
||||
if (firstVideoPublication?.subscribed == true &&
|
||||
firstVideoPublication?.muted == false &&
|
||||
_visible) {
|
||||
return firstVideoPublication?.track;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Widget> above() => [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
if (firstVideoPublication != null)
|
||||
RemoteTrackPublicationMenuWidget(
|
||||
pub: firstVideoPublication!,
|
||||
icon: EvaIcons.videoOutline,
|
||||
),
|
||||
// Menu for Audio RemoteTrackPublication
|
||||
if (firstAudioPublication != null)
|
||||
RemoteTrackPublicationMenuWidget(
|
||||
pub: firstAudioPublication!,
|
||||
icon: EvaIcons.volumeUpOutline,
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
class RemoteTrackPublicationMenuWidget extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final RemoteTrackPublication pub;
|
||||
|
||||
Reference in New Issue
Block a user