why am I meeting this erroor ?
flutter: Connection error: LiveKit Exception: [TrackCreateException] Failed to create stream, at least 1 video or audio track should exist
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:livekit_client/livekit_client.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(MaterialApp(home: LiveKitVideoCanvasApp()));
}
class LiveKitTokenResponse {
final String accessToken;
const LiveKitTokenResponse({required this.accessToken});
factory LiveKitTokenResponse.fromJson(Map<String, dynamic> json) {
return LiveKitTokenResponse(accessToken: json['accessToken'] as String);
}
}
class LiveKitVideoCanvasApp extends StatefulWidget {
const LiveKitVideoCanvasApp({Key? key}) : super(key: key);
@override
_LiveKitVideoCanvasAppState createState() => _LiveKitVideoCanvasAppState();
}
class _LiveKitCanvasPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint =
Paint()
..color = Colors.blue.withOpacity(0.3)
..style = PaintingStyle.fill;
canvas.drawRect(Offset.zero & size, paint);
final circlePaint =
Paint()
..color = Colors.white.withOpacity(0.5)
..style = PaintingStyle.fill;
canvas.drawCircle(Offset(size.width / 2, size.height / 2), 50, circlePaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
class _LiveKitVideoCanvasAppState extends State<LiveKitVideoCanvasApp> {
Room? _room;
LocalVideoTrack? _localVideoTrack;
bool _isConnected = false;
CustomPaint? _customCanvas;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('LiveKit Video with Canvas')),
body: Column(
children: [
// Video and Canvas View
_isConnected
? SizedBox(
width: 300,
height: 400,
child:
_customCanvas ??
Container(child: Text("Connection in progress")),
)
: const Center(child: Text('Not Connected')),
// Connect Button
ElevatedButton(
onPressed: _isConnected ? null : _connectToRoom,
child: const Text('Connect to Room'),
),
],
),
);
}
@override
void dispose() {
_room?.disconnect();
_localVideoTrack?.dispose();
super.dispose();
}
Future<String> getLivekitToken() async {
try {
http.Response response = await http.get(
Uri.parse(
"
https://example.ngrok-free.app/api/livekit-token",
),
headers: {"ngrok-skip-browser-warning": "true"},
);
if (response.statusCode == 200) {
LiveKitTokenResponse liveKitTokenResponse =
LiveKitTokenResponse.fromJson(
jsonDecode(response.body) as Map<String, dynamic>,
);
return liveKitTokenResponse.accessToken;
} else {
throw Exception('Failed to get LiveKit token');
}
} catch (e) {
print('Token retrieval error: $e');
rethrow;
}
}
@override
void initState() {
super.initState();
_initializeLiveKit();
}
Future<void> _connectToRoom() async {
try {
// Ensure video track exists
_localVideoTrack ??= await LocalVideoTrack.createCameraTrack();
// Create room if not exists
_room ??= Room();
// Get LiveKit token
String livekitToken = await getLivekitToken();
// Connect to room
await _room!.prepareConnection(
'
wss://example.livekit.cloud',
livekitToken,
);
await _room!.connect(
'
wss://example.livekit.cloud',
livekitToken,
roomOptions: const RoomOptions(adaptiveStream: true, dynacast: true),
);
// Publish local video track
await _room!.localParticipant?.publishVideoTrack(_localVideoTrack!);
setState(() {
_isConnected = true;
_customCanvas = CustomPaint(
painter: _LiveKitCanvasPainter(),
child: VideoTrackRenderer(_localVideoTrack!),
);
});
} catch (error) {
print('Connection error: $error');
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Connection failed: $error')));
}
}
Future<void> _initializeLiveKit() async {
// Request camera and microphone permissions
final cameraStatus = await Permission.camera.request();
final micStatus = await Permission.microphone.request();
if (cameraStatus.isGranted && micStatus.isGranted) {
try {
// Create video track immediately
_localVideoTrack = await LocalVideoTrack.createCameraTrack();
} catch (e) {
print('Failed to create video track: $e');
}
}
}
}