flutter_webrtc_fixed_v0_12_0 0.12.2
flutter_webrtc_fixed_v0_12_0: ^0.12.2 copied to clipboard
Flutter WebRTC plugin for iOS/Android/Destkop/Web, based on GoogleWebRTC. Fixed version with improvements and bug fixes.
import 'package:flutter/material.dart';
import 'package:flutter_webrtc_fixed_v0_12_0/flutter_webrtc_fixed_v0_12_0.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter WebRTC Fixed Example',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: const WebRTCExample(),
);
}
}
class WebRTCExample extends StatefulWidget {
const WebRTCExample({super.key});
@override
State<WebRTCExample> createState() => _WebRTCExampleState();
}
class _WebRTCExampleState extends State<WebRTCExample> {
final RTCVideoRenderer _localRenderer = RTCVideoRenderer();
final RTCVideoRenderer _remoteRenderer = RTCVideoRenderer();
RTCPeerConnection? _peerConnection;
MediaStream? _localStream;
@override
void initState() {
super.initState();
_initRenderers();
}
Future<void> _initRenderers() async {
await _localRenderer.initialize();
await _remoteRenderer.initialize();
}
Future<void> _createPeerConnection() async {
final configuration = <String, dynamic>{
'iceServers': [
{'urls': 'stun:stun.l.google.com:19302'},
]
};
_peerConnection = await createPeerConnection(configuration);
_peerConnection!.onIceCandidate = (RTCIceCandidate candidate) {
print('Got candidate: ${candidate.toMap()}');
};
_peerConnection!.onConnectionState = (RTCPeerConnectionState state) {
print('Connection state: $state');
setState(() {
// Update UI based on connection state
});
};
_peerConnection!.onAddStream = (MediaStream stream) {
print('Got remote stream: ${stream.id}');
_remoteRenderer.srcObject = stream;
};
}
Future<void> _getUserMedia() async {
final Map<String, dynamic> mediaConstraints = {
'audio': true,
'video': {
'mandatory': {
'minWidth': '640',
'minHeight': '480',
'minFrameRate': '30',
},
'facingMode': 'user',
'optional': [],
}
};
_localStream = await navigator.mediaDevices.getUserMedia(mediaConstraints);
_localRenderer.srcObject = _localStream;
}
Future<void> _startCall() async {
await _createPeerConnection();
await _getUserMedia();
if (_localStream != null) {
await _peerConnection!.addStream(_localStream!);
}
final offer = await _peerConnection!.createOffer();
await _peerConnection!.setLocalDescription(offer);
}
Future<void> _hangUp() async {
await _peerConnection?.close();
_peerConnection = null;
_localRenderer.srcObject = null;
_remoteRenderer.srcObject = null;
}
@override
void dispose() {
_localRenderer.dispose();
_remoteRenderer.dispose();
_hangUp();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebRTC Fixed Example'),
backgroundColor: Colors.blue[600],
foregroundColor: Colors.white,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Text(
'WebRTC Video Call Demo',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
Expanded(
child: Row(
children: [
Expanded(
child: Column(
children: [
const Text(
'Local Video',
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w600),
),
const SizedBox(height: 10),
Container(
height: 200,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: RTCVideoView(
_localRenderer,
objectFit: RTCVideoViewObjectFit
.RTCVideoViewObjectFitCover,
),
),
),
],
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
children: [
const Text(
'Remote Video',
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w600),
),
const SizedBox(height: 10),
Container(
height: 200,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(8),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: RTCVideoView(
_remoteRenderer,
objectFit: RTCVideoViewObjectFit
.RTCVideoViewObjectFitCover,
),
),
),
],
),
),
],
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton.icon(
onPressed: _startCall,
icon: const Icon(Icons.videocam),
label: const Text('Start Call'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 12),
),
),
ElevatedButton.icon(
onPressed: _hangUp,
icon: const Icon(Icons.call_end),
label: const Text('Hang Up'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 12),
),
),
],
),
const SizedBox(height: 20),
const Card(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Instructions:',
style:
TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text('1. Tap "Start Call" to begin video capture'),
Text('2. Grant camera and microphone permissions'),
Text('3. Your local video will appear in the left panel'),
Text(
'4. For a real call, you would need signaling to exchange offers/answers'),
Text('5. Tap "Hang Up" to end the call'),
],
),
),
),
],
),
),
);
}
}