voxa_rtc_engine 0.1.0
voxa_rtc_engine: ^0.1.0 copied to clipboard
Agora-API-compatible real-time voice/video SDK for Flutter. Drop-in replacement for agora_rtc_engine (compat line: 6.x, pinned to 6.6.3), powered by a LiveKit-based media engine.
example/lib/main.dart
// Agora's official Flutter video-call quickstart, running on VoxaRTC.
//
// Diff vs the Agora original (the migration promise, plan §1):
// 1. the package import below (was: package:agora_rtc_engine/...)
// 2. the two VOXA-marked config lines (the only non-Agora addition)
// Everything else — names, signatures, callbacks, widgets — is unchanged.
import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:voxa_rtc_engine/voxa_rtc_engine.dart'; // ← was agora_rtc_engine
import 'package:voxa_rtc_engine/voxa.dart'; // VOXA: gateway config import
// Fill in from your VoxaRTC deployment (dev defaults match infra/docker).
// Token minted 2026-08-07, wildcard uid, valid 24h — re-mint with:
// cd voxa-compat-gateway && go run ./cmd/tokentool -channel demo -uid 0 -expire 86400
const appId = '970CA35de60c44645bbae8a215061b33';
const token =
'007eJxSYDh6uurXFN74aIsoXy3vjR+WfvjY9FU0wXNp5KOmwH6/FGsFBktzA2dHY9OUVDODZBMTMxPTpKTEVItEI0NTAzPDJGNj/8slWQ2BjAyVXd7MjAyMDCwMjAwgPhOYZAaTLFAyJTU3n4EBEAAA//+ygyHi';
const channel = 'demo';
void main() {
// VOXA: point the SDK at your Compat Gateway (LAN IP when using a device).
VoxaRtc.serverUrl = 'http://192.168.68.53:8788';
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int? _remoteUid;
bool _localUserJoined = false;
late RtcEngine _engine;
@override
void initState() {
super.initState();
initAgora();
}
Future<void> initAgora() async {
// retrieve permissions
// VOXA: guarded for the macOS dev harness — permission_handler has no
// desktop implementation; macOS prompts natively on first device use.
if (Platform.isAndroid || Platform.isIOS) {
await [Permission.microphone, Permission.camera].request();
}
// create the engine
_engine = createAgoraRtcEngine();
await _engine.initialize(const RtcEngineContext(
appId: appId,
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
));
_engine.registerEventHandler(
RtcEngineEventHandler(
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
debugPrint('local user ${connection.localUid} joined');
setState(() {
_localUserJoined = true;
});
},
onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
debugPrint('remote user $remoteUid joined');
setState(() {
_remoteUid = remoteUid;
});
},
onUserOffline: (RtcConnection connection, int remoteUid,
UserOfflineReasonType reason) {
debugPrint('remote user $remoteUid left channel');
setState(() {
_remoteUid = null;
});
},
onTokenPrivilegeWillExpire: (RtcConnection connection, String token) {
debugPrint(
'[onTokenPrivilegeWillExpire] connection: ${connection.toJson()}, token: $token');
},
),
);
await _engine.setClientRole(role: ClientRoleType.clientRoleBroadcaster);
await _engine.enableVideo();
await _engine.startPreview();
await _engine.joinChannel(
token: token,
channelId: channel,
uid: 0,
options: const ChannelMediaOptions(),
);
}
@override
void dispose() {
super.dispose();
_dispose();
}
Future<void> _dispose() async {
await _engine.leaveChannel();
await _engine.release();
}
// Create UI with local view and remote view
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('VoxaRTC Video Call'),
),
body: Stack(
children: [
Center(
child: _remoteVideo(),
),
Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 100,
height: 150,
child: Center(
child: _localUserJoined
? AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine,
canvas: const VideoCanvas(uid: 0),
),
)
: const CircularProgressIndicator(),
),
),
),
],
),
),
);
}
// Display remote user's video
Widget _remoteVideo() {
if (_remoteUid != null) {
return AgoraVideoView(
controller: VideoViewController.remote(
rtcEngine: _engine,
canvas: VideoCanvas(uid: _remoteUid),
connection: const RtcConnection(channelId: channel),
),
);
} else {
return const Text(
'Please wait for remote user to join',
textAlign: TextAlign.center,
);
}
}
}