You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.8 KiB
6.8 KiB
Realtime Plugin 使用示例
基本用法
import 'package:realtime/realtime.dart';
class VoiceCallPage extends StatefulWidget {
@override
_VoiceCallPageState createState() => _VoiceCallPageState();
}
class _VoiceCallPageState extends State<VoiceCallPage> {
final RealtimeService _realtimeService = RealtimeService();
StreamSubscription<RealtimeEvent>? _eventSubscription;
ConnectionStatus _connectionStatus = ConnectionStatus.disconnected;
VoiceStatus _voiceStatus = VoiceStatus.idle;
@override
void initState() {
super.initState();
_setupEventListener();
_initializeService();
}
void _setupEventListener() {
_eventSubscription = _realtimeService.eventStream.listen((event) {
switch (event.type) {
case 'connectionStatusChanged':
setState(() {
_connectionStatus = _parseConnectionStatus(event.data);
});
break;
case 'voiceStatusChanged':
setState(() {
_voiceStatus = _parseVoiceStatus(event.data);
});
break;
case 'textReceived':
print('收到文本: ${event.data}');
break;
case 'error':
print('错误: ${event.data}');
break;
}
});
}
Future<void> _initializeService() async {
try {
final success = await _realtimeService.initialize(
serverUrl: 'wss://your-vocode-server.com/ws',
sampleRate: 16000,
channels: 1,
bitsPerSample: 16,
);
if (success) {
print('Realtime服务初始化成功');
} else {
print('Realtime服务初始化失败');
}
} catch (e) {
print('初始化错误: $e');
}
}
Future<void> _connect() async {
try {
final success = await _realtimeService.connect();
if (!success) {
print('连接失败');
}
} catch (e) {
print('连接错误: $e');
}
}
Future<void> _disconnect() async {
try {
await _realtimeService.disconnect();
} catch (e) {
print('断开连接错误: $e');
}
}
Future<void> _startRecording() async {
try {
final success = await _realtimeService.startRecording();
if (!success) {
print('开始录音失败');
}
} catch (e) {
print('录音错误: $e');
}
}
Future<void> _stopRecording() async {
try {
final success = await _realtimeService.stopRecording();
if (!success) {
print('停止录音失败');
}
} catch (e) {
print('停止录音错误: $e');
}
}
ConnectionStatus _parseConnectionStatus(String status) {
switch (status) {
case 'connected': return ConnectionStatus.connected;
case 'connecting': return ConnectionStatus.connecting;
case 'disconnected': return ConnectionStatus.disconnected;
case 'error': return ConnectionStatus.error;
default: return ConnectionStatus.disconnected;
}
}
VoiceStatus _parseVoiceStatus(String status) {
switch (status) {
case 'recording': return VoiceStatus.recording;
case 'processing': return VoiceStatus.processing;
case 'playing': return VoiceStatus.playing;
case 'idle': return VoiceStatus.idle;
default: return VoiceStatus.idle;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('实时语音聊天'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 连接状态显示
Text('连接状态: ${_connectionStatus.name}'),
SizedBox(height: 20),
// 语音状态显示
Text('语音状态: ${_voiceStatus.name}'),
SizedBox(height: 40),
// 连接按钮
ElevatedButton(
onPressed: _connectionStatus == ConnectionStatus.disconnected
? _connect
: _disconnect,
child: Text(_connectionStatus == ConnectionStatus.disconnected
? '连接'
: '断开'),
),
SizedBox(height: 20),
// 录音按钮
ElevatedButton(
onPressed: _connectionStatus == ConnectionStatus.connected
? (_voiceStatus == VoiceStatus.recording
? _stopRecording
: _startRecording)
: null,
child: Text(_voiceStatus == VoiceStatus.recording
? '停止录音'
: '开始录音'),
),
],
),
),
);
}
@override
void dispose() {
_eventSubscription?.cancel();
_realtimeService.dispose();
super.dispose();
}
}
GetX控制器中的使用
import 'package:get/get.dart';
import 'package:realtime/realtime.dart';
class VoiceController extends GetxController {
final RealtimeService _realtimeService = RealtimeService();
final RxBool isConnected = false.obs;
final RxBool isRecording = false.obs;
StreamSubscription<RealtimeEvent>? _eventSubscription;
@override
void onInit() {
super.onInit();
_setupEventListener();
_initializeService();
}
void _setupEventListener() {
_eventSubscription = _realtimeService.eventStream.listen((event) {
switch (event.type) {
case 'connectionStatusChanged':
isConnected.value = event.data == 'connected';
break;
case 'voiceStatusChanged':
isRecording.value = event.data == 'recording';
break;
}
});
}
Future<void> _initializeService() async {
await _realtimeService.initialize(
serverUrl: 'wss://your-server/ws',
);
}
Future<void> toggleConnection() async {
if (isConnected.value) {
await _realtimeService.disconnect();
} else {
await _realtimeService.connect();
}
}
Future<void> toggleRecording() async {
if (isRecording.value) {
await _realtimeService.stopRecording();
} else {
await _realtimeService.startRecording();
}
}
@override
void onClose() {
_eventSubscription?.cancel();
_realtimeService.dispose();
super.onClose();
}
}
权限配置
iOS
在 ios/Runner/Info.plist 中添加:
<key>NSMicrophoneUsageDescription</key>
<string>应用需要麦克风权限进行语音录制</string>
服务器配置示例
需要一个支持WebSocket的Vocode服务器,具体实现可参考Vocode官方文档。
服务器需要:
- 接收16kHz/16-bit/单声道的PCM音频数据
- 返回相同格式的音频数据
- 支持文本消息交换
注意事项
- 确保网络连接稳定
- 音频格式必须与服务器保持一致
- 及时处理事件流中的错误
- 在适当时机释放资源