enx_voice_bot 1.0.6+7
enx_voice_bot: ^1.0.6+7 copied to clipboard
Flutter SDK for real-time Gen-AI Voice Bots via REST call control and Pipecat WebSocket, powered by EnableX Dialogs.
example/lib/main.dart
import 'package:enx_voice_bot/enx_voice_client.dart';
import 'package:enx_voice_bot/enx_voice_listener.dart';
import 'package:enx_voice_bot/enx_voice_state.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: VoiceCallDemo(),
);
}
}
class VoiceCallDemo extends StatefulWidget {
@override
_VoiceCallDemoState createState() => _VoiceCallDemoState();
}
class _VoiceCallDemoState extends State<VoiceCallDemo> implements EnxVoiceListener {
late EnxVoiceClient _voiceClient;
var isConnected=false;
var isPress=false;
var isMutePress=false;
var virtualNumber="911100123457";//virtual number
/// Optional override for POST /call base URL. If empty, [EnxSdkConfig] default is used.
static const String callApiBaseUrl = '';
var _showDtmfKeypad = false;
@override
void initState() {
super.initState();
_voiceClient = EnxVoiceClient()
.init(virtualNumber)
.setEnxVoiceListener(this)
.setOnDtmfKeypadChanged((show) {
if (!mounted) return;
setState(() => _showDtmfKeypad = show);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Voice Call'),centerTitle: true,),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(isConnected ? 'Status: Connected' :isPress?'Status : Connecting': 'Status: Connect'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async{
if(isConnected||isPress){
_voiceClient.disconnect();
}else{
_voiceClient.connect(
callApiBaseUrl:
callApiBaseUrl.isNotEmpty ? callApiBaseUrl : null,
);
setState(() {
isPress=true;
});
}
},
child: Text(isConnected ? 'Disconnect' :isPress?'Disconnect': 'Connect'),
),
ElevatedButton(
onPressed: () async{
if(isConnected && isMutePress){
_voiceClient.muteAudio(false);
}else{
_voiceClient.muteAudio(true);
}
},
child: Text(isMutePress?'UnMute':'Mute'),
),
const SizedBox(height: 20),
if (_showDtmfKeypad && isConnected) ...[
const Text('DTMF', style: TextStyle(fontWeight: FontWeight.w600)),
const SizedBox(height: 8),
Wrap(
spacing: 6,
runSpacing: 6,
alignment: WrapAlignment.center,
children: [
for (final d in ['1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '0', '#'])
SizedBox(
width: 56,
child: OutlinedButton(
onPressed: () async {
final ok = await _voiceClient.sendDtmf(d);
debugPrint('DTMF $d sent: $ok');
},
child: Text(d),
),
),
],
),
],
],
),
),
);
}
@override
void onCallConnected() {
print('Call connected!');
setState(() {
isConnected=true;
isPress=false;
isMutePress=false;
});
}
@override
void onCallDisconnect() {
print('Call disconnected');
setState(() {
isConnected=false;
isPress=false;
isMutePress=false;
_showDtmfKeypad = false;
});
}
@override
void onError(String message) {
print('Error: $message');
}
@override
void onMuteStateChanged(bool isMuted) {
print('Mute: $isMuted');
setState(() {
isMutePress= isMuted;
});
}
@override
void onStatus(EnxVoiceState state) {
print('Status: ${state.name}');
}
@override
void onUserSpeaking(bool isUserSpeaking) {
print('User speaking: $isUserSpeaking');
}
@override
void onBotSpeaking(bool isBotSpeaking) {
print('Bot speaking: $isBotSpeaking');
}
@override
void dispose() {
super.dispose();
}
}