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