Skip to main content
Version: 5.x.x

Mute / Unmute Mic - Flutter

This feature allows hosts to communicate with each other during the livestream by enabling or disabling their microphones. While only hosts (participants in SEND_AND_RECV mode) can speak, audience members (in RECV_ONLY mode) can listen to the conversation in real time.

unmuteMic()​

  • By using the unmuteMic() function of the Room class, the host can publish their audio to other hosts and audience members.

  • You can also pass a customised audio track in the unmuteMic() method by using Custom Audio Track.

  • Audio stream of the participant can be accessed from the streams map of the Participant object.

  • room.isMicEnabled tells you whether your own mic is on.

muteMic()​

  • By using the muteMic() function of the Room class, the host can stop publishing their audio to other hosts and audience members.

unmuteMic() and muteMic() return a Future that throws a VideoSDKError when the SDK refuses the call, for example 3054 when the mic is already on or off, or 3055 while another mic change is still running. await them inside try/catch. On Android, a call made while the livestream reconnects is reported on Events.error instead of being thrown, so keep an Events.error handler too.

Events associated with unmuteMic​

  • Every Participant will receive a callback on Events.streamEnabled of the Room object with RTCStream object. stream.participantId is the id of the host who unmuted.

Events associated with muteMic​

  • Every Participant will receive a callback on Events.streamDisabled of the Room object with RTCStream object. stream.participantId is the id of the host who muted.

streamEnabled and streamDisabled also reach handlers added with participant.on() on the participant who owns the stream.

import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';

class ParticipantTile extends StatefulWidget {
final Room room;
final Participant participant;
const ParticipantTile(
{super.key, required this.room, required this.participant});

@override
State<ParticipantTile> createState() => _ParticipantTileState();
}

class _ParticipantTileState extends State<ParticipantTile> {
RTCStream? audioStream;

@override
void initState() {
super.initState();
// initial audio stream for the participant
//Check if audio stream is already present
audioStream = widget.participant.streams.values
.where((stream) => stream.kind == MediaType.AUDIO)
.firstOrNull;

_initStreamListeners();
}

//Change state according to the events received
void _initStreamListeners() {
widget.room.on(Events.streamEnabled, _onStreamEnabled);
widget.room.on(Events.streamDisabled, _onStreamDisabled);
}

bool _isAudioOfThisParticipant(RTCStream stream) =>
stream.participantId == widget.participant.id && stream.kind == MediaType.AUDIO;

void _onStreamEnabled(RTCStream stream) {
if (_isAudioOfThisParticipant(stream)) {
//Mic Turned On
setState(() => audioStream = stream);
}
}

void _onStreamDisabled(RTCStream stream) {
if (_isAudioOfThisParticipant(stream)) {
//Mic Turned Off
setState(() => audioStream = null);
}
}

@override
void dispose() {
// The room outlives the tile, so remove the handlers from it.
widget.room.off(Events.streamEnabled, _onStreamEnabled);
widget.room.off(Events.streamDisabled, _onStreamDisabled);
super.dispose();
}

Future<void> unmuteMic() async {
try {
await widget.room.unmuteMic();
} on VideoSDKError catch (error) {
print("unmuteMic failed: ${error.code} ${error.name}");
}
}

Future<void> muteMic() async {
try {
await widget.room.muteMic();
} on VideoSDKError catch (error) {
print("muteMic failed: ${error.code} ${error.name}");
}
}

@override
Widget build(BuildContext context) {
return Column(
children: [
Text("Mic of ${widget.participant.displayName} is ${audioStream != null ? "on" : "off"}"),
// The buttons act on your own mic, so show them on your own tile.
if (widget.participant.isLocal) ...[
ElevatedButton(
onPressed: unmuteMic,
child: const Text("Unmute Mic"),
),
ElevatedButton(
onPressed: muteMic,
child: const Text("Mute Mic"),
),
],
],
);
}
}

Audio Permissions​

  • If permissions are not granted, use the requestPermissions() method of the VideoSDK class to prompt users to grant access to their devices.
  • On Android, the SDK does not ask for microphone permission. Call requestPermissions() before unmuteMic(). Without the permission, the mic stays off: unmuteMic() throws error 3018, and the same error reaches Events.error.
  • To enable requesting of microphone permission on iOS devices, add the following to your Info.plist, located at <project root>/ios/Runner/Info.plist:
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) Microphone Usage!</string>
note

In case permissions are blocked by the user, the permission request dialogue cannot be re-rendered programmatically. In such cases, consider providing guidance to users on manually adjusting their permissions.

Future<void> requestMediaPermissions() async {
try {
//For requesting just audio permission.
Map<String, bool> reqAudioPermissions =
await VideoSDK.requestPermissions(Permissions.AUDIO);
if (reqAudioPermissions['audio'] != true) {
// The user did not allow the microphone.
}
} catch (ex) {
print("Error in requestPermission ");
}
}
  • Requesting permissions if not already granted:

API Reference​

The API references for all the methods and events utilised in this guide are provided below.

Got a Question? Ask us on discord