QuickRTC Flutter Client
A Flutter WebRTC library built on MediaSoup for real-time video conferencing.
Features
- Simple API - High-level methods for common tasks
- Cross-Platform - Android, iOS, macOS, Web
- Screen Sharing - Full support for all platforms
- Auto-Consume - Automatically handles new participant streams
Installation
dependencies:
quickrtc_flutter_client: ^1.1.0
Quick Start (Simplest Way)
Use the QuickRTCConference widget for a batteries-included experience:
import 'package:quickrtc_flutter_client/quickrtc_flutter_client.dart';
QuickRTCConference(
serverUrl: 'https://your-server.com:3000',
conferenceId: 'my-room',
participantName: 'John',
onJoined: (controller) => controller.enableMedia(),
builder: (context, state, controller) {
return Column(
children: [
// Video grid
Expanded(
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount: state.participantList.length + 1,
itemBuilder: (_, index) {
if (index == 0) {
return QuickRTCMediaRenderer(
stream: state.localVideoStream?.stream,
mirror: true,
isLocal: true,
);
}
final p = state.participantList[index - 1];
return QuickRTCMediaRenderer(
remoteStream: p.videoStream,
participantName: p.name,
);
},
),
),
// Controls using new convenience getters
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(state.isLocalAudioActive ? Icons.mic : Icons.mic_off),
onPressed: () => controller.toggleMicrophoneMute(),
),
IconButton(
icon: Icon(state.isLocalVideoActive ? Icons.videocam : Icons.videocam_off),
onPressed: () => controller.toggleCameraPause(),
),
IconButton(
icon: Icon(state.isLocalScreenshareActive ? Icons.stop_screen_share : Icons.screen_share),
onPressed: () => controller.toggleScreenShareWithPicker(context),
),
],
),
],
);
},
)
Quick Start (Controller API)
For more control, use QuickRTCController.connect():
// Connect and join in one step
final controller = await QuickRTCController.connect(
serverUrl: 'https://your-server.com:3000',
conferenceId: 'my-room',
participantName: 'John',
);
// Enable camera and microphone
await controller.enableMedia();
// Later: leave and cleanup (socket auto-disconnects)
await controller.leaveMeeting();
controller.dispose();
Manual Setup (Full Control)
import 'package:quickrtc_flutter_client/quickrtc_flutter_client.dart';
import 'package:socket_io_client/socket_io_client.dart' as io;
// Create socket (or use QuickRTCSocket.connect() for simpler setup)
final socket = await QuickRTCSocket.connect('https://your-server.com:3000');
// Create controller
final controller = QuickRTCController(socket: socket, debug: true);
// Wrap your app with QuickRTCProvider
QuickRTCProvider(
controller: controller,
child: const MyApp(),
);
// Join meeting
await controller.joinMeeting(
conferenceId: 'my-room',
participantName: 'John',
);
Builder, Consumer & Listener Patterns
The SDK provides several widgets to simplify state management and UI updates.
QuickRTCBuilder
Use this for reactive UI updates. It only rebuilds when the specified condition is met.
QuickRTCBuilder(
buildWhen: (prev, curr) => prev.participantCount != curr.participantCount,
builder: (context, state) {
return Text('Participants: ${state.participantCount}');
},
)
QuickRTCConsumer
Use this when you need both the controller and the state in your build method.
QuickRTCConsumer(
builder: (context, controller, state, child) {
return IconButton(
icon: Icon(state.isLocalAudioActive ? Icons.mic : Icons.mic_off),
onPressed: () => controller.toggleMicrophoneMute(),
);
},
)
QuickRTCListener
Use this for occasional side effects like showing notifications or navigation, without rebuilding the UI.
QuickRTCListener(
listenWhen: (prev, curr) => prev.error != curr.error && curr.error != null,
listener: (context, controller) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(controller.state.error!)),
);
},
child: const YourWidget(),
)
Rendering Media
QuickRTCMediaRenderer
Powerful widget for rendering local and remote video streams.
// Local video with mirroring
QuickRTCMediaRenderer(
stream: state.localVideoStream?.stream,
mirror: true,
isLocal: true,
participantName: 'You',
)
// Remote video from participant
QuickRTCMediaRenderer(
remoteStream: participant.videoStream,
participantName: participant.name,
isAudioEnabled: participant.hasAudio && !participant.isAudioMuted,
isVideoEnabled: participant.hasVideo && !participant.isVideoMuted,
)
QuickRTCAudioRenderers
Invisible widget that handles audio playback for all remote participants.
QuickRTCAudioRenderers(participants: state.participantList)
Note:
QuickRTCConferenceincludes this automatically whenautoRenderAudio: true(default).
Screen Sharing
// Simplest: use toggleScreenShareWithPicker (handles platform detection)
await controller.toggleScreenShareWithPicker(context);
// Or manually:
// Mobile
final media = await QuickRTCStatic.getLocalMedia(MediaConfig.screenShareOnly());
// Desktop (with picker)
final media = await QuickRTCStatic.getScreenShareWithPicker(context);
// Publish
await controller.produce(
ProduceInput.fromTrack(media.screenshareTrack!, type: StreamType.screenshare),
);
Android Screen Share External Stop Detection
On Android, when screen sharing is stopped via the system notification ("Stop now" button) or when MediaProjection is revoked, the SDK automatically detects this and:
- Stops the local screen share
- Emits
closeProducerto the server - Other participants are notified and remove the screen share tile
This is handled automatically - no additional code is required. The SDK uses multiple detection strategies:
- Track
onMutecallback - Track
mutedproperty monitoring - Producer RTP stats monitoring (detects when no new bytes are being sent)
Leave Meeting
await controller.leaveMeeting();
controller.dispose();
// If using manual socket setup:
socket.disconnect();
Platform Setup
Android
android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Required for screen sharing on Android 10+ -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Required for screen sharing on Android 14+ (API 34+) -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
android/app/build.gradle:
android {
defaultConfig {
minSdkVersion 24
}
}
Note: On Android 14+, the SDK handles the foreground service requirement automatically. The service includes a notification with a "Stop" button for users to stop screen sharing.
iOS
ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>Camera for video calls</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone for audio calls</string>
macOS
macos/Runner/*.entitlements:
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
State Properties
// Connection
state.isConnected // Connection status
state.participantList // List<RemoteParticipant>
state.participantCount // Number of participants
// Local media presence
state.hasLocalAudio // Mic active
state.hasLocalVideo // Camera active
state.hasLocalScreenshare // Screen share active
// Local media paused state
state.isLocalAudioPaused // Mic muted
state.isLocalVideoPaused // Camera paused
state.isLocalScreensharePaused // Screen share paused
// NEW in 1.1.0: Convenience getters (active = present && not paused)
state.isLocalAudioActive // hasLocalAudio && !isLocalAudioPaused
state.isLocalVideoActive // hasLocalVideo && !isLocalVideoPaused
state.isLocalScreenshareActive // hasLocalScreenshare && !isLocalScreensharePaused
// Streams
state.localVideoStream // LocalStream?
state.localAudioStream // LocalStream?
state.localScreenshareStream // LocalStream?
RemoteParticipant
participant.id // String
participant.name // String
participant.videoStream // RemoteStream?
participant.audioStream // RemoteStream?
participant.hasVideo // bool
participant.hasAudio // bool
participant.isVideoMuted // bool
participant.isAudioMuted // bool
What's New in 1.1.0
- Version consolidation - Single unified version for all components
- Android screen share external stop detection - Automatically detects when screen sharing is stopped via system notification and notifies other participants
- Producer stats monitoring - Reliable detection of stalled screen share streams
QuickRTCConferencewidget - Batteries-included widget that handles socket, controller lifecycle, and audio renderingQuickRTCController.connect()- One-liner to connect and joinQuickRTCSocket.connect()- Simplified socket connectiontoggleScreenShareWithPicker(context)- Platform-aware screen sharing toggle- Convenience getters -
isLocalAudioActive,isLocalVideoActive,isLocalScreenshareActive
Example
See example/ for code snippets demonstrating basic features. For a complete working application, refer to the QuickRTC Example Repository.
Documentation
Full documentation: quickrtc-docs.vercel.app/docs/flutter/getting-started
Libraries
- models/consumer_params
- models/participant_info
- models/socket_response
- models/transport_options
- platform/quickrtc_platform
- quickrtc_flutter_client
- QuickRTC Flutter Client Library
- state/quick_rtc_state
- types
- widgets/quick_rtc_audio_renderer
- widgets/quick_rtc_builder
- widgets/quick_rtc_conference
- widgets/quick_rtc_consumer
- widgets/quick_rtc_listener
- widgets/quick_rtc_media_renderer
- widgets/quick_rtc_provider
- widgets/quick_rtc_theme
- widgets/rtc_video_renderer_widget
- widgets/screen_select_dialog