Demo App
Here is the code for a sample application made using this package.

GitHub Link:

Folder Structure

1
lib
2
┣ logic
3
┃ ┗ blocs
4
┃ ┃ ┣ consumers
5
┃ ┃ ┃ ┣ consumers_bloc.dart
6
┃ ┃ ┃ ┣ consumers_event.dart
7
┃ ┃ ┃ ┗ consumers_state.dart
8
┃ ┃ ┣ me
9
┃ ┃ ┃ ┣ me_bloc.dart
10
┃ ┃ ┃ ┣ me_event.dart
11
┃ ┃ ┃ ┗ me_state.dart
12
┃ ┃ ┣ media_devices
13
┃ ┃ ┃ ┣ media_devices_bloc.dart
14
┃ ┃ ┃ ┣ media_devices_event.dart
15
┃ ┃ ┃ ┗ media_devices_state.dart
16
┃ ┃ ┣ peers
17
┃ ┃ ┃ ┣ peers_bloc.dart
18
┃ ┃ ┃ ┣ peers_event.dart
19
┃ ┃ ┃ ┗ peers_state.dart
20
┃ ┃ ┣ producers
21
┃ ┃ ┃ ┣ producers_bloc.dart
22
┃ ┃ ┃ ┣ producers_event.dart
23
┃ ┃ ┃ ┗ producers_state.dart
24
┃ ┃ ┗ room
25
┃ ┃ ┃ ┣ room_bloc.dart
26
┃ ┃ ┃ ┣ room_event.dart
27
┃ ┃ ┃ ┗ room_state.dart
28
┣ presentation
29
┃ ┣ components
30
┃ ┃ ┣ list_media_devices
31
┃ ┃ ┃ ┗ list_media_devices.dart
32
┃ ┃ ┣ me
33
┃ ┃ ┃ ┣ microphone.dart
34
┃ ┃ ┃ ┣ renderMe.dart
35
┃ ┃ ┃ ┗ webcam.dart
36
┃ ┃ ┗ others
37
┃ ┃ ┃ ┣ other.dart
38
┃ ┃ ┃ ┗ renderOthers.dart
39
┃ ┣ enter_page.dart
40
┃ ┣ room.dart
41
┃ ┗ voice_audio_settings.dart
42
┗ main.dart
Copied!

main.dart

1
import 'dart:developer' as dev;
2
import 'dart:math';
3
4
import 'package:english_words/english_words.dart';
5
import 'package:flutter_bloc/flutter_bloc.dart';
6
import 'package:huddle01_flutter/huddle01_flutter.dart';
7
import 'package:huddle01_flutter_example/logic/blocs/consumers/consumers_bloc.dart';
8
import 'package:huddle01_flutter_example/logic/blocs/me/me_bloc.dart';
9
import 'package:huddle01_flutter_example/logic/blocs/media_devices/media_devices_bloc.dart';
10
import 'package:huddle01_flutter_example/logic/blocs/peers/peers_bloc.dart';
11
import 'package:huddle01_flutter_example/logic/blocs/producers/producers_bloc.dart';
12
import 'package:huddle01_flutter_example/logic/blocs/room/room_bloc.dart';
13
import 'package:huddle01_flutter_example/presentation/enter_page.dart';
14
import 'package:huddle01_flutter_example/presentation/room.dart';
15
import 'package:random_string/random_string.dart';
16
17
import 'package:flutter/material.dart';
18
19
void main() {
20
runApp(BlocProvider<MediaDevicesBloc>(
21
create: (context) => MediaDevicesBloc()..add(MediaDeviceLoadDevices()),
22
lazy: false,
23
child: MyApp()));
24
}
25
26
class MyApp extends StatelessWidget {
27
_setupEventListeners({
28
required ConsumersBloc consumersBloc,
29
required ProducersBloc producersBloc,
30
required PeersBloc peersBloc,
31
required MeBloc meBloc,
32
}) {
33
emitter.on('addConsumer', (consumer) {
34
consumersBloc.add(ConsumerAdd(consumer: consumer));
35
});
36
emitter.on('removeConsumer', (consumerId) {
37
consumersBloc.add(ConsumerRemove(consumerId: consumerId));
38
});
39
emitter.on('addProducer', (producer) {
40
producersBloc.add(ProducerAdd(producer: producer));
41
if (producer.source == 'webcam') {
42
meBloc.add(MeSetWebcamInProgress(progress: true));
43
}
44
});
45
emitter.on('removeProducer', (source) {
46
producersBloc.add(ProducerRemove(source: source));
47
if (source == 'webcam') {
48
meBloc.add(MeSetWebcamInProgress(progress: false));
49
}
50
});
51
emitter.on('addPeer', (peer) {
52
peersBloc.add(PeerAdd(newPeer: peer));
53
});
54
emitter.on('removePeer', (peerId) {
55
peersBloc.add(PeerRemove(peerId: peerId));
56
});
57
emitter.on('addPeerConsumer', (consumer) {
58
peersBloc.add(
59
PeerAddConsumer(peerId: consumer.peerId, consumerId: consumer.id));
60
});
61
emitter.on('removePeerConsumer', (peerId, consumerId) {
62
peersBloc.add(PeerRemoveConsumer(peerId: peerId, consumerId: consumerId));
63
});
64
}
65
66
@override
67
Widget build(BuildContext context) {
68
return MaterialApp(
69
initialRoute: '/',
70
// ignore: missing_return
71
onGenerateRoute: (settings) {
72
if (settings.name == EnterPage.RoutePath) {
73
return MaterialPageRoute(
74
builder: (context) => EnterPage(),
75
);
76
}
77
if (settings.name == '/room') {
78
return MaterialPageRoute(
79
builder: (context) => MultiBlocProvider(
80
// just like multiprovider, we are providing all the blocs except the media devices one to the Room
81
providers: [
82
BlocProvider<ProducersBloc>(
83
lazy: false,
84
create: (context) => ProducersBloc(),
85
),
86
BlocProvider<ConsumersBloc>(
87
lazy: false,
88
create: (context) => ConsumersBloc(),
89
),
90
BlocProvider<PeersBloc>(
91
lazy: false,
92
create: (context) => PeersBloc(
93
consumersBloc: context.read<ConsumersBloc>(),
94
),
95
),
96
BlocProvider<MeBloc>(
97
lazy: false,
98
create: (context) => MeBloc(
99
displayName: nouns[Random.secure().nextInt(2500)],
100
id: randomAlpha(8)),
101
),
102
BlocProvider<RoomBloc>(
103
lazy: false,
104
create: (context) =>
105
RoomBloc(settings.arguments.toString()),
106
),
107
],
108
child: RepositoryProvider<HuddleClientRepository>(
109
// provider to provide room client as an object to all the child widgets of this
110
lazy: false,
111
create: (context) {
112
_setupEventListeners(
113
consumersBloc: context.read<ConsumersBloc>(),
114
producersBloc: context.read<ProducersBloc>(),
115
peersBloc: context.read<PeersBloc>(),
116
meBloc: context.read<MeBloc>(),
117
);
118
MediaDevicesBloc mediaDevicesBloc =
119
context.read<MediaDevicesBloc>();
120
String audioInputDeviceId =
121
mediaDevicesBloc.state.selectedAudioInput!.deviceId;
122
String videoInputDeviceId =
123
mediaDevicesBloc.state.selectedVideoInput!.deviceId;
124
final meState = context.read<MeBloc>().state;
125
String displayName = meState.displayName;
126
String id = meState.id;
127
final roomState = context.read<RoomBloc>().state;
128
String roomId = roomState.roomId!;
129
130
return HuddleClientRepository(
131
peerId: id,
132
displayName: displayName,
133
apiKey: "i4pzqbpxza8vpijQMwZsP1H7nZZEH0TN3vR4NdNS",
134
roomId: roomId,
135
audioInputDeviceId: audioInputDeviceId,
136
videoInputDeviceId: videoInputDeviceId,
137
)..join();
138
},
139
child: Room(),
140
)),
141
);
142
}
143
},
144
);
145
}
146
}
147
Copied!

UI and screens

room.dart
enter_page.dart
voice_audio_settings.dart
list_media_devices.dart
others/other.dart
others/render_other.dart
me/microphone.dart
me/webcam.dart
me/render_me.dart
1
import 'dart:async';
2
3
import 'package:flutter/material.dart';
4
import 'package:flutter/services.dart';
5
import 'package:flutter_bloc/flutter_bloc.dart';
6
import 'package:huddle01_flutter/huddle01_flutter.dart';
7
import 'package:huddle01_flutter_example/logic/blocs/media_devices/media_devices_bloc.dart';
8
import 'package:huddle01_flutter_example/logic/blocs/room/room_bloc.dart';
9
import 'package:huddle01_flutter_example/presentation/components/me/renderMe.dart';
10
import 'package:huddle01_flutter_example/presentation/components/others/renderOthers.dart';
11
import 'package:huddle01_flutter_example/presentation/voice_audio_settings.dart';
12
13
class Room extends StatefulWidget {
14
const Room({Key? key}) : super(key: key);
15
16
@override
17
_RoomState createState() => _RoomState();
18
}
19
20
class _RoomState extends State<Room> {
21
late StreamSubscription<MediaDevicesState> _mediaDevicesBlocSubscription;
22
late String audioInputDeviceId;
23
late String videoInputDeviceId;
24
25
@override
26
void dispose() {
27
super.dispose();
28
_mediaDevicesBlocSubscription.cancel();
29
}
30
31
@override
32
void initState() {
33
super.initState();
34
audioInputDeviceId =
35
context.read<MediaDevicesBloc>().state.selectedAudioInput!.deviceId;
36
videoInputDeviceId =
37
context.read<MediaDevicesBloc>().state.selectedVideoInput!.deviceId;
38
_mediaDevicesBlocSubscription = context
39
.read<MediaDevicesBloc>()
40
.stream
41
.listen((MediaDevicesState state) async {
42
if (state.selectedAudioInput != null &&
43
state.selectedAudioInput!.deviceId != audioInputDeviceId) {
44
await context.read<HuddleClientRepository>().disableMic();
45
context.read<HuddleClientRepository>().enableMic();
46
}
47
48
if (state.selectedVideoInput != null &&
49
state.selectedVideoInput!.deviceId != videoInputDeviceId) {
50
await context.read<HuddleClientRepository>().disableWebcam();
51
context.read<HuddleClientRepository>().enableWebcam();
52
}
53
});
54
}
55
56
setStreamListener(context) {}
57
58
@override
59
Widget build(BuildContext context) {
60
return Scaffold(
61
appBar: AppBar(
62
title: Builder(
63
builder: (context) {
64
String roomId = context.select((RoomBloc bloc) => bloc.state.roomId!);
65
return Text(roomId);
66
},
67
),
68
actions: [
69
IconButton(
70
onPressed: () {
71
String roomId = context.read<RoomBloc>().state.roomId!;
72
Clipboard.setData(ClipboardData(text: roomId));
73
ScaffoldMessenger.of(context).showSnackBar(
74
SnackBar(
75
content: const Text('Room link copied to clipboard'),
76
duration: const Duration(seconds: 1),
77
),
78
);
79
},
80
icon: Icon(Icons.copy),
81
),
82
IconButton(
83
onPressed: () {
84
Navigator.push(
85
context,
86
MaterialPageRoute(
87
builder: (_) => BlocProvider.value(
88
value: context.read<MediaDevicesBloc>(),
89
child: AudioVideoSettings(),
90
),
91
),
92
);
93
},
94
icon: Icon(Icons.settings),
95
),
96
],
97
leading: IconButton(
98
icon: Icon(Icons.arrow_back),
99
onPressed: () {
100
context.read<HuddleClientRepository>().close();
101
Navigator.pop(context);
102
},
103
),
104
),
105
body: Stack(
106
fit: StackFit.expand,
107
children: [
108
RenderOther(),
109
RenderMe(),
110
],
111
),
112
);
113
}
114
}
115
Copied!
1
import 'package:flutter/material.dart';
2
import 'package:flutter_bloc/flutter_bloc.dart';
3
import 'package:huddle01_flutter/huddle01_flutter.dart';
4
import 'package:huddle01_flutter_example/logic/blocs/media_devices/media_devices_bloc.dart';
5
import 'package:huddle01_flutter_example/presentation/components/list_media_devices/list_media_devices.dart';
6
7
class EnterPage extends StatefulWidget {
8
static const String RoutePath = '/';
9
10
@override
11
_EnterPageState createState() => _EnterPageState();
12
}
13
14
class _EnterPageState extends State<EnterPage> {
15
final TextEditingController _textEditingController = TextEditingController();
16
17
@override
18
void initState() {
19
super.initState();
20
21
_textEditingController.addListener(() {
22
final String text = _textEditingController.text.toLowerCase();
23
setState(() {
24
_textEditingController.value = _textEditingController.value.copyWith(
25
text: text,
26
);
27
});
28
});
29
}
30
31
@override
32
void dispose() {
33
_textEditingController.dispose();
34
super.dispose();
35
}
36
37
@override
38
Widget build(BuildContext context) {
39
return Scaffold(
40
appBar: AppBar(
41
title: Text('mediasoup-client-flutter'),
42
),
43
body: Container(
44
alignment: Alignment.center,
45
child: Column(
46
children: [
47
TextField(
48
autofocus: false,
49
controller: _textEditingController,
50
decoration: InputDecoration(
51
border: OutlineInputBorder(),
52
floatingLabelBehavior: FloatingLabelBehavior.always,
53
hintText: 'Room url (empty = random room)',
54
),
55
),
56
ElevatedButton(
57
onPressed: () {
58
Navigator.pushNamed(
59
context,
60
'/room',
61
arguments: _textEditingController.value.text.toLowerCase(),
62
);
63
},
64
child: Text('Join'),
65
),
66
Text(
67
'Audio Input',
68
style: Theme.of(context).textTheme.headline5,
69
),
70
Builder(
71
builder: (context) {
72
final List<MediaDeviceInfo> audioInputDevices = context
73
.select((MediaDevicesBloc bloc) => bloc.state.audioInputs);
74
final MediaDeviceInfo? selectedAudioInput = context.select(
75
(MediaDevicesBloc bloc) => bloc.state.selectedAudioInput);
76
77
return ListMediaDevices(
78
key: Key('audioinput'),
79
selectedDevice: selectedAudioInput,
80
devices: audioInputDevices,
81
onSelect: (MediaDeviceInfo device) => context
82
.read<MediaDevicesBloc>()
83
.add(MediaDeviceSelectAudioInput(device)),
84
);
85
},
86
),
87
Text(
88
'Audio Output',
89
style: Theme.of(context).textTheme.headline5,
90
),
91
Builder(
92
builder: (context) {
93
final List<MediaDeviceInfo> audioOutputDevices = context
94
.select((MediaDevicesBloc bloc) => bloc.state.audioOutputs);
95
final MediaDeviceInfo? selectedAudioOutput = context.select(
96
(MediaDevicesBloc bloc) => bloc.state.selectedAudioOutput);
97
98
return ListMediaDevices(
99
key: Key('audiooutput'),
100
selectedDevice: selectedAudioOutput,
101
devices: audioOutputDevices,
102
onSelect: (MediaDeviceInfo device) => context
103
.read<MediaDevicesBloc>()
104
.add(MediaDeviceSelectAudioOutput(device)),
105
);
106
},
107
),
108
Text(
109
'Video Input',
110
style: Theme.of(context).textTheme.headline5,
111
),
112
Builder(
113
builder: (context) {
114
final List<MediaDeviceInfo> videoInputDevices = context
115
.select((MediaDevicesBloc bloc) => bloc.state.videoInputs);
116
final MediaDeviceInfo? selectedVideoInput = context.select(
117
(MediaDevicesBloc bloc) => bloc.state.selectedVideoInput);
118
119
return ListMediaDevices(
120
key: Key('videoinput'),
121
selectedDevice: selectedVideoInput,
122
devices: videoInputDevices,
123
onSelect: (MediaDeviceInfo device) => context
124
.read<MediaDevicesBloc>()
125
.add(MediaDeviceSelectVideoInput(device)),
126
);
127
},
128
),
129
],
130
),
131
),
132
);
133
}
134
}
135
Copied!
1
import 'package:flutter/material.dart';
2
import 'package:flutter_bloc/flutter_bloc.dart';
3
import 'package:huddle01_flutter/huddle01_flutter.dart';
4
import 'package:huddle01_flutter_example/logic/blocs/media_devices/media_devices_bloc.dart';
5
import 'package:huddle01_flutter_example/presentation/components/list_media_devices/list_media_devices.dart';
6
7
class AudioVideoSettings extends StatefulWidget {
8
const AudioVideoSettings({Key? key}) : super(key: key);
9
10
@override
11
_AudioVideoSettingsState createState() => _AudioVideoSettingsState();
12
}
13
14
class _AudioVideoSettingsState extends State<AudioVideoSettings> {
15
MediaDeviceInfo? selectedAudioInput;
16
MediaDeviceInfo? selectedAudioOutput;
17
MediaDeviceInfo? selectedVideoInput;
18
19
@override
20
void initState() {
21
super.initState();
22
23
final MediaDevicesState devicesInfo =
24
context.read<MediaDevicesBloc>().state;
25
setState(() {
26
selectedAudioInput = devicesInfo.selectedAudioInput;
27
selectedAudioOutput = devicesInfo.selectedAudioOutput;
28
selectedVideoInput = devicesInfo.selectedVideoInput;
29
});
30
}
31
32
@override
33
Widget build(BuildContext context) {
34
return Scaffold(
35
appBar: AppBar(
36
title: Text('Audio & Video'),
37
actions: [
38
Builder(
39
builder: (context) => IconButton(
40
onPressed: () {
41
final MediaDevicesState devicesInfo =
42
context.read<MediaDevicesBloc>().state;
43
final MediaDeviceInfo? audioInput =
44
devicesInfo.selectedAudioInput;
45
final MediaDeviceInfo? audioOutput =
46
devicesInfo.selectedAudioOutput;
47
final MediaDeviceInfo? videoInput =
48
devicesInfo.selectedVideoInput;
49
50
if (audioInput != selectedAudioInput) {
51
context
52
.read<MediaDevicesBloc>()
53
.add(MediaDeviceSelectAudioInput(selectedAudioInput!));
54
}
55
if (audioOutput != selectedAudioOutput) {
56
context
57
.read<MediaDevicesBloc>()
58
.add(MediaDeviceSelectAudioOutput(selectedAudioOutput));
59
}
60
if (videoInput != selectedVideoInput) {
61
context
62
.read<MediaDevicesBloc>()
63
.add(MediaDeviceSelectVideoInput(selectedVideoInput!));
64
}
65
Navigator.pop(context);
66
},
67
icon: Icon(
68
Icons.save,
69
),
70
),
71
),
72
],
73
),
74
body: Container(
75
alignment: Alignment.center,
76
child: Column(
77
children: [
78
Builder(
79
builder: (context) {
80
final List<MediaDeviceInfo> audioInputDevices = context
81
.select((MediaDevicesBloc bloc) => bloc.state.audioInputs);
82
83
return ListMediaDevices(
84
key: Key('audioinput'),
85
selectedDevice: selectedAudioInput!,
86
devices: audioInputDevices,
87
onSelect: (MediaDeviceInfo device) => setState(() {
88
selectedAudioInput = device;
89
}),
90
);
91
},
92
),
93
Text(
94
'Audio Output',
95
style: Theme.of(context).textTheme.headline5,
96
),
97
Builder(
98
builder: (context) {
99
final List<MediaDeviceInfo> audioOutputDevices = context
100
.select((MediaDevicesBloc bloc) => bloc.state.audioOutputs);
101
102
return ListMediaDevices(
103
key: Key('audiooutput'),
104
selectedDevice: selectedAudioOutput,
105
devices: audioOutputDevices,
106
onSelect: (MediaDeviceInfo device) => setState(() {
107
selectedAudioOutput = device;
108
}),
109
);
110
},
111
),
112
Text(
113
'Video Input',
114
style: Theme.of(context).textTheme.headline5,
115
),
116
Builder(
117
builder: (context) {
118
final List<MediaDeviceInfo> videoInputDevices = context
119
.select((MediaDevicesBloc bloc) => bloc.state.videoInputs);
120
121
return ListMediaDevices(
122
key: Key('videoinput'),
123
selectedDevice: selectedVideoInput!,
124
devices: videoInputDevices,
125
onSelect: (MediaDeviceInfo device) => setState(() {
126
selectedVideoInput = device;
127
}),
128
);
129
},
130
),
131
],
132
),
133
),
134
);
135
}
136
}
137
Copied!
1
import 'package:flutter/material.dart';
2
import 'package:huddle01_flutter/huddle01_flutter.dart';
3
4
class ListMediaDevices extends StatelessWidget {
5
final List<MediaDeviceInfo> devices;
6
final MediaDeviceInfo? selectedDevice;
7
final Function(MediaDeviceInfo) onSelect;
8
const ListMediaDevices({
9
Key? key,
10
required this.devices,
11
this.selectedDevice,
12
required this.onSelect,
13
}) : super(key: key);
14
15
void selectDevice(int index) {
16
if (selectedDevice != devices[index]) {
17
onSelect(devices[index]);
18
}
19
}
20
21
@override
22
Widget build(BuildContext context) {
23
if (devices.isNotEmpty) {
24
return ListView.builder(
25
shrinkWrap: true,
26
scrollDirection: Axis.vertical,
27
itemCount: devices.length,
28
itemBuilder: (context, index) => ListTile(
29
key: Key(devices[index].deviceId),
30
title: Text(devices[index].label),
31
subtitle: Text('id: ${devices[index].deviceId}'),
32
selected: selectedDevice == devices[index],
33
onTap: () => selectDevice(index),
34
),
35
);
36
}
37
38
return Text('No devices');
39
}
40
}
41
Copied!
1
import 'package:flutter/material.dart';
2
import 'package:huddle01_flutter/huddle01_flutter.dart';
3
4
class Other extends StatelessWidget {
5
final Peer peer;
6
final Consumer? video;
7
final RTCVideoRenderer? renderer;
8
9
const Other({Key? key, required this.peer, this.video, this.renderer})
10
: super(key: key);
11
12
@override
13
Widget build(BuildContext context) {
14
return Container(
15
height: 250,
16
width: 300,
17
color: Colors.black87,
18
child: Stack(
19
fit: StackFit.expand,
20
children: [
21
if (video != null && renderer != null)
22
RTCVideoView(renderer!)
23
else
24
Container(
25
height: 250,
26
width: 300,
27
decoration: BoxDecoration(color: Colors.black54),
28
),
29
Positioned(
30
bottom: 5,
31
left: 2,
32
child: Column(
33
mainAxisSize: MainAxisSize.min,
34
crossAxisAlignment: CrossAxisAlignment.start,
35
children: [
36
Container(
37
child: Text(
38
'${peer.displayName}\n${peer.device!.name} ${peer.device!.version}',
39
style: TextStyle(
40
color: Colors.white,
41
),
42
),
43
decoration: BoxDecoration(
44
color: Colors.black87,
45
borderRadius: const BorderRadius.all(Radius.circular(8)),
46
),
47
padding: const EdgeInsets.all(8),
48
),
49
],
50
))
51
],
52
),
53
);
54
}
55
}
56
Copied!
1
import 'package:flutter/material.dart';
2
import 'package:flutter_bloc/flutter_bloc.dart';
3
import 'package:huddle01_flutter/huddle01_flutter.dart';
4
import 'package:huddle01_flutter_example/logic/blocs/consumers/consumers_bloc.dart';
5
import 'package:huddle01_flutter_example/logic/blocs/peers/peers_bloc.dart';
6
import 'package:huddle01_flutter_example/presentation/components/others/other.dart';
7
8
class RenderOther extends StatelessWidget {
9
const RenderOther({Key? key}) : super(key: key);
10
11
@override
12
Widget build(BuildContext context) {
13
final Map<String, Peer> peers =
14
context.select((PeersBloc bloc) => bloc.state.peers);
15
final Map<String, Consumer> consumers =
16
context.select((ConsumersBloc bloc) => bloc.state.consumers);
17
final Map<String, RTCVideoRenderer> renderers =
18
context.select((ConsumersBloc bloc) => bloc.state.renderers);
19
20
return GridView.count(
21
crossAxisCount: 2,
22
scrollDirection: Axis.vertical,
23
shrinkWrap: true,
24
children: peers.values.map((Peer peer) {
25
if (peer.consumers.isNotEmpty) {
26
String? id = peer.consumers.firstWhere(
27
(cId) => consumers[cId]?.kind == 'video',
28
orElse: () => 'null',
29
);
30
if (id != 'null') {
31
return Other(
32
key: Key(peer.id! + id),
33
peer: peer,
34
video: consumers[id]!,
35
renderer: renderers[id]!,
36
);
37
}
38
}
39
return Other(
40
key: Key(peer.id!),
41
peer: peer,
42
);
43
}).toList(),
44
);
45
}
46
}
47
Copied!
1
import 'package:flutter/material.dart';
2
import 'package:flutter_bloc/flutter_bloc.dart';
3
import 'package:huddle01_flutter/huddle01_flutter.dart';
4
import 'package:huddle01_flutter_example/logic/blocs/producers/producers_bloc.dart';
5
6
class Microphone extends StatefulWidget {
7
const Microphone({Key? key}) : super(key: key);
8
9
@override
10
_MicrophoneState createState() => _MicrophoneState();
11
}
12
13
class _MicrophoneState extends State<Microphone> {
14
@override
15
Widget build(BuildContext context) {
16
return BlocBuilder<ProducersBloc, ProducersState>(
17
builder: (context, state) {
18
if (state.mic == null)
19
return IconButton(
20
icon: Icon(
21
Icons.mic_off,
22
color: Colors.grey,
23
),
24
onPressed: () {},
25
);
26
return IconButton(
27
onPressed: () {
28
if (state.mic!.paused) {
29
context.read<HuddleClientRepository>().unmuteMic();
30
setState(() {});
31
} else {
32
context.read<HuddleClientRepository>().muteMic();
33
setState(() {});
34
}
35
},
36
icon: Icon(state.mic!.paused ? Icons.mic_off : Icons.mic));
37
},
38
);
39
}
40
}
41
Copied!
1
import 'package:flutter/material.dart';
2
import 'package:flutter_bloc/flutter_bloc.dart';
3
import 'package:huddle01_flutter/huddle01_flutter.dart';
4
import 'package:huddle01_flutter_example/logic/blocs/me/me_bloc.dart';
5
import 'package:huddle01_flutter_example/logic/blocs/producers/producers_bloc.dart';
6
7
class Webcam extends StatefulWidget {
8
const Webcam({Key? key}) : super(key: key);
9
10
@override
11
_WebcamState createState() => _WebcamState();
12
}
13
14
class _WebcamState extends State<Webcam> {
15
@override
16
Widget build(BuildContext context) {
17
bool inProgress =
18
context.select((MeBloc bloc) => bloc.state.webcamInProgress);
19
20
return BlocBuilder<ProducersBloc, ProducersState>(
21
builder: (context, state) {
22
// if (state.webcam == null) return IconButton(icon: Icon(Icons.videocam_off, color: Colors.grey,),);
23
return IconButton(
24
onPressed: () {
25
// if (inProgress) {
26
// return;
27
// }
28
if (state.webcam != null) {
29
context.read<HuddleClientRepository>().disableWebcam();
30
setState(() {});
31
} else {
32
context.read<HuddleClientRepository>().enableWebcam();
33
setState(() {});
34
}
35
},
36
icon: Icon(
37
state.webcam == null ? Icons.videocam_off : Icons.videocam));
38
},
39
);
40
}
41
}
42
Copied!
1
import 'dart:developer';
2
3
import 'package:flutter/material.dart';
4
import 'package:flutter_bloc/flutter_bloc.dart';
5
import 'package:huddle01_flutter/huddle01_flutter.dart';
6
import 'package:huddle01_flutter_example/logic/blocs/producers/producers_bloc.dart';
7
import 'package:huddle01_flutter_example/presentation/components/me/microphone.dart';
8
import 'package:huddle01_flutter_example/presentation/components/me/webcam.dart';
9
10
class RenderMe extends StatefulWidget {
11
const RenderMe({Key? key}) : super(key: key);
12
13
@override
14
_RenderMeState createState() => _RenderMeState();
15
}
16
17
class _RenderMeState extends State<RenderMe> {
18
late RTCVideoRenderer renderer;
19
20
@override
21
void initState() {
22
super.initState();
23
initRenderers();
24
}
25
26
@override
27
Widget build(BuildContext context) {
28
try {
29
log('starting render me');
30
return BlocConsumer<ProducersBloc, ProducersState>(
31
listener: (context, state) {
32
log('inside listener');
33
try {
34
renderer.srcObject = state.webcam!.stream;
35
log(renderer.toString());
36
} catch (e) {
37
log(e.toString());
38
}
39
},
40
builder: (context, state) {
41
log('inside bloc consumer');
42
try {
43
return Align(
44
alignment: Alignment.bottomLeft,
45
child: Stack(
46
alignment: Alignment.centerRight,
47
children: [
48
Container(
49
width: MediaQuery.of(context).size.width * .3,
50
height: MediaQuery.of(context).size.height * .3,
51
margin: const EdgeInsets.only(left: 5, bottom: 10),
52
decoration: BoxDecoration(
53
color: Colors.black54,
54
borderRadius: BorderRadius.circular(8),
55
shape: BoxShape.rectangle,
56
border: Border.all(
57
color: Colors.black,
58
width: 2.0,
59
style: BorderStyle.solid,
60
),
61
),
62
child: RTCVideoView(
63
renderer,
64
// mirror: true,
65
),
66
),
67
Positioned(
68
top: 5,
69
right: 5,
70
child: Row(
71
mainAxisSize: MainAxisSize.min,
72
mainAxisAlignment: MainAxisAlignment.spaceAround,
73
children: [
74
Microphone(),
75
Webcam(),
76
],
77
),
78
)
79
],
80
),
81
);
82
} catch (e) {
83
log(e.toString());
84
return Container();
85
}
86
},
87
);
88
} catch (e) {
89
log(e.toString());
90
return Container();
91
}
92
}
93
94
void initRenderers() async {
95
renderer = RTCVideoRenderer();
96
await renderer.initialize();
97
}
98
99
@override
100
void dispose() {
101