update example (no casting)

This commit is contained in:
Hiroshi Horie
2021-12-02 14:21:41 +07:00
parent a409ed0aca
commit 64a243a312
2 changed files with 121 additions and 47 deletions
+2 -3
View File
@@ -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),
), ),
), ),
), ),
+119 -44
View File
@@ -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;