Building a Flutter App with Huddle01
Walkthrough
The following guide explains how you can integrate audio/video into your Flutter mobile-app seamlessly using the Huddle01 Flutter SDK.
Installation
Install huddle01_flutter_client
in your flutter app
To power your Flutter dApp with audio communication using Huddle01 install the following:
flutter pub add huddle01_flutter_client
Initialization of project
Head over to API Keys Page and connect your wallet to get your project credentials:
API Key
projectId
Once done, initialize your project by calling the initialize()
method and pass projectId
in params.
String projectId = 'YOUR-PROJECT-ID';
String roomId = 'YOUR-ROOM-ID';
// Initialize your huddleClient
HuddleClient huddleClient = HuddleClient();
// Initialize project
huddleClient.initialize(projectId);
Joining the lobby
Room Id can be generated using the : Create Room API
Add the following TextButton
to your jsx and call the joinLobby()
method.
import 'package:flutter/material.dart';
import 'package:huddle01_flutter_client/huddle_client.dart';
class MeetingScreen extends StatefulWidget {
const MeetingScreen({super.key});
@override
State<MeetingScreen> createState() => _MeetingScreenState();
}
class _MeetingScreenState extends State<MeetingScreen> {
HuddleClient huddleClient = HuddleClient();
String roomId = 'MY-ROOM-ID';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("My Meeting App"),
),
body: Column(children: [
TextButton(
child: const Text('JOIN-LOBBY'),
onPressed: () {
if (huddleClient.isJoinLobbyCallable()) {
huddleClient.joinLobby(roomId);
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('JOIN-LOBBY -> not callable yet'),
backgroundColor: Colors.red,
elevation: 4,
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.all(5),
duration: Duration(seconds: 1),
));
}
},
),
]),
);
}
}
Joining and leaving the room
Add the joinRoom()
and leaveRoom()
methods for joining and leaving functionalities.
import 'package:flutter/material.dart';
import 'package:huddle01_flutter_client/huddle_client.dart';
class MeetingScreen extends StatefulWidget {
const MeetingScreen({super.key});
@override
State<MeetingScreen> createState() => _MeetingScreenState();
}
class _MeetingScreenState extends State<MeetingScreen> {
HuddleClient huddleClient = HuddleClient();
String roomId = 'MY-ROOM-ID';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("My Meeting App"),
),
body: Column(children: [
TextButton(
child: const Text('JOIN-LOBBY'),
onPressed: () {
if (huddleClient.isJoinLobbyCallable()) {
huddleClient.joinLobby(roomId);
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('JOIN-LOBBY -> not callable yet'),
backgroundColor: Colors.red,
elevation: 4,
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.all(5),
duration: Duration(seconds: 1),
));
}
},
),
TextButton(
child: const Text('FETCH-AUDIO'),
onPressed: () {
if (huddleClient.isFetchAudioStreamCallable()) {
huddleClient.fetchAudioStream();
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('FETCH-AUDIO -> not callable yet'),
backgroundColor: Colors.red,
elevation: 4,
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.all(5),
duration: Duration(seconds: 1),
));
}
},
TextButton(
child: const Text('JOIN-ROOM'),
onPressed: () {
huddleClient.joinRoom(roomId);
},
),
TextButton(
child: const Text('LEAVE-ROOM'),
onPressed: () {
huddleClient.leaveRoom();
},
),
]),
);
}
}
Clicking on the JOIN_ROOM button, will make the user join the room and will allow them to send/receive media with other participants.
Sending media across to other participants
Here, we are using a term PRODUCE which means sending your audio/video stream across to the other peer who will CONSUME (or receive) the streams.
import 'package:flutter/material.dart';
import 'package:huddle01_flutter_client/huddle_client.dart';
class MeetingScreen extends StatefulWidget {
const MeetingScreen({super.key});
@override
State<MeetingScreen> createState() => _MeetingScreenState();
}
class _MeetingScreenState extends State<MeetingScreen> {
HuddleClient huddleClient = HuddleClient();
String roomId = 'MY-ROOM-ID';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("My Meeting App"),
),
body: Column(children: [
TextButton(
child: const Text('JOIN-LOBBY'),
onPressed: () {
if (huddleClient.isJoinLobbyCallable()) {
huddleClient.joinLobby(roomId);
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('JOIN-LOBBY -> not callable yet'),
backgroundColor: Colors.red,
elevation: 4,
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.all(5),
duration: Duration(seconds: 1),
));
}
},
),
TextButton(
child: const Text('FETCH-AUDIO'),
onPressed: () {
if (huddleClient.isFetchAudioStreamCallable()) {
huddleClient.fetchAudioStream();
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('FETCH-AUDIO -> not callable yet'),
backgroundColor: Colors.red,
elevation: 4,
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.all(5),
duration: Duration(seconds: 1),
));
}
},
TextButton(
child: const Text('JOIN-ROOM'),
onPressed: () {
huddleClient.joinRoom(roomId);
},
),
TextButton(
child: const Text('LEAVE-ROOM'),
onPressed: () {
huddleClient.leaveRoom();
},
),
TextButton(
child: const Text('PRODUCE_AUDIO'),
onPressed: () {
huddleClient.produceAudio(stream);
},
),
TextButton(
child: const Text('STOP-PRODUCING-AUDIO'),
onPressed: () {
huddleClient.stopProducingAudio();
},
),
]),
);
}
}
Receiving the audio and video streams
Here, we are using a term PRODUCE which means sending your audio/video stream across to the other peer who will CONSUME (or receive) the streams.
Getting access to remote video/audio streams
JSX:
// remote-stream
RTCVideoRenderer? remoteRenderer;
initilialize() async {
remoteRenderer = RTCVideoRenderer();
await remoteRenderer!.initialize();
remoteRenderer!.srcObject = huddleClient.getRemoteStream();
}
const Text(
"Local Stream",
style: TextStyle(fontSize: 18),
),
Padding(
padding: const EdgeInsets.fromLTRB(25, 0, 25, 0),
child: Container(
color: Colors.grey,
width: 500,
height: 250,
child: huddleClient.getRenderer() != null
? RTCVideoView(
huddleClient.getRenderer()!,
objectFit:
RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
)
: null),
),
TextButton(
child: const Text(
'Get Remote Stream',
style: TextStyle(fontSize: 18),
),
onPressed: () {
initilialize();
},
),
Padding(
padding: const EdgeInsets.fromLTRB(25, 0, 25, 0),
child: Container(
color: Colors.grey,
width: 500,
height: 250,
child: huddleClient.getConsumers().isNotEmpty
? RTCVideoView(
remoteRenderer!,
objectFit:
RTCVideoViewObjectFit.RTCVideoViewObjectFitCover,
)
: null),
),
You're all set! Happy Hacking! 🎉
For more information, please refer to the SDK Reference.