Skip to content

Commit

Permalink
update.
Browse files Browse the repository at this point in the history
  • Loading branch information
cloudwebrtc committed Nov 27, 2023
1 parent 0488642 commit dab2673
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 31 deletions.
2 changes: 1 addition & 1 deletion example/lib/pages/connect.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class _ConnectPageState extends State<ConnectPage> {
bool _fastConnect = false;
bool _e2ee = false;
bool _multiCodec = false;
bool _autoSubscribe = true;
bool _autoSubscribe = false;
String _preferredCodec = 'Preferred Codec';
String _backupCodec = 'VP8';

Expand Down
221 changes: 191 additions & 30 deletions example/lib/pages/room.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import 'dart:convert';
import 'dart:math' as math;

import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:livekit_client/livekit_client.dart';
import 'package:visibility_detector/visibility_detector.dart';

import '../exts.dart';
import '../widgets/controls.dart';
Expand All @@ -24,14 +26,17 @@ class RoomPage extends StatefulWidget {
State<StatefulWidget> createState() => _RoomPageState();
}

class _RoomPageState extends State<RoomPage> {
class _RoomPageState extends State<RoomPage> with WidgetsBindingObserver {
//
List<ParticipantTrack> participantTracks = [];
EventsListener<RoomEvent> get _listener => widget.listener;
bool get fastConnection => widget.room.engine.fastConnectOptions != null;
bool gridView = true;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);

// add callback for a `RoomEvent` as opposed to a `ParticipantEvent`
widget.room.addListener(_onRoomDidUpdate);
// add callbacks for finer grained events
Expand All @@ -56,9 +61,41 @@ class _RoomPageState extends State<RoomPage> {
await _listener.dispose();
await widget.room.dispose();
})();

WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

AppLifecycleState? _notification;

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_notification = state;
});
if (state == AppLifecycleState.resumed) {
for (var p in participantTracks) {
if (p.participant.hasVideo &&
participantSubscriptions.containsKey(p.participant.identity)) {
(p.participant as RemoteParticipant)
.videoTracks
.firstOrNull
?.subscribe();
}
}
} else if (state == AppLifecycleState.paused) {
for (var p in participantTracks) {
if (p.participant.hasVideo &&
participantSubscriptions.containsKey(p.participant.identity)) {
(p.participant as RemoteParticipant)
.videoTracks
.firstOrNull
?.unsubscribe();
}
}
}
}

/// for more information, see [event types](https://docs.livekit.io/client/events/#events)
void _setUpListeners() => _listener
..on<RoomDisconnectedEvent>((event) async {
Expand Down Expand Up @@ -210,42 +247,166 @@ class _RoomPageState extends State<RoomPage> {
});
}

void subscribeToVideoTracks(RemoteParticipant participant) async {
if (participantSubscriptions[participant.identity] == true) {
return;
}
participantSubscriptions[participant.identity] = true;
await participant.videoTracks.firstOrNull?.subscribe();
}

void unSubscribeToVideoTracks(RemoteParticipant participant) async {
if (participantSubscriptions[participant.identity] == false) {
return;
}
participantSubscriptions[participant.identity] = false;
await participant.videoTracks.firstOrNull?.unsubscribe();
}

Map<String, bool> visibleParticipants = {};
Map<String, bool> participantSubscriptions = {};

@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(
'Room: ${widget.room.name} $_notification',
style: const TextStyle(color: Colors.white),
),
actions: [
IconButton(
icon: Icon(
Icons.view_module,
color: gridView ? Colors.green : Colors.white,
),
onPressed: () {
setState(() {
gridView = true;
});
},
),
IconButton(
icon: Icon(
Icons.view_sidebar,
color: !gridView ? Colors.green : Colors.white,
),
onPressed: () {
setState(() {
gridView = false;
});
},
),
SizedBox.fromSize(
size: const Size(42, 10),
)
],
),
body: Stack(
children: [
Column(
children: [
Expanded(
child: participantTracks.isNotEmpty
? ParticipantWidget.widgetFor(participantTracks.first,
showStatsLayer: true)
: Container()),
if (widget.room.localParticipant != null)
SafeArea(
top: false,
child: ControlsWidget(
widget.room, widget.room.localParticipant!),
)
],
),
Positioned(
if (gridView)
CustomScrollView(
slivers: [
SliverGrid(
gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 300.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 1.5,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
final participant =
participantTracks[index].participant;

return VisibilityDetector(
key: Key(participant.identity),
onVisibilityChanged: (info) {
final bool isVisible = info.visibleFraction > 0;
final bool isCompletelyGone =
info.visibleFraction == 0;
final bool isSubscribed = participantSubscriptions[
participant.identity] ??
false;
final bool shouldSubscribe = !isSubscribed &&
isVisible &&
participant is! LocalParticipant;

if (shouldSubscribe) {
subscribeToVideoTracks(
participant as RemoteParticipant,
);
visibleParticipants[participant.identity] = true;
} else if (participant is! LocalParticipant &&
isCompletelyGone) {
unSubscribeToVideoTracks(
participant as RemoteParticipant,
);
visibleParticipants.remove(
participant.identity,
);
}
},
child: SizedBox(
width: 240,
height: 180,
child: ParticipantWidget.widgetFor(
participantTracks[index],
),
),
);
},
childCount: participantTracks.length,
),
),
],
),
if (widget.room.localParticipant != null && gridView)
Positioned(
left: 0,
right: 0,
top: 0,
child: SizedBox(
height: 120,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: math.max(0, participantTracks.length - 1),
itemBuilder: (BuildContext context, int index) => SizedBox(
width: 180,
height: 120,
child: ParticipantWidget.widgetFor(
participantTracks[index + 1]),
bottom: 20,
child: SafeArea(
top: false,
child: ControlsWidget(
widget.room, widget.room.localParticipant!),
),
),
if (!gridView)
Column(
children: [
Expanded(
child: participantTracks.isNotEmpty
? ParticipantWidget.widgetFor(participantTracks.first,
showStatsLayer: true)
: Container()),
if (widget.room.localParticipant != null)
SafeArea(
top: false,
child: ControlsWidget(
widget.room, widget.room.localParticipant!),
)
],
),
if (!gridView)
Positioned(
left: 0,
right: 0,
top: 0,
child: SizedBox(
height: 120,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: math.max(0, participantTracks.length - 1),
itemBuilder: (BuildContext context, int index) =>
SizedBox(
width: 180,
height: 120,
child: ParticipantWidget.widgetFor(
participantTracks[index + 1]),
),
),
),
)),
)),
],
),
);
Expand Down

0 comments on commit dab2673

Please sign in to comment.