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

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官方文档。

服务器需要:

  1. 接收16kHz/16-bit/单声道的PCM音频数据
  2. 返回相同格式的音频数据
  3. 支持文本消息交换

注意事项

  1. 确保网络连接稳定
  2. 音频格式必须与服务器保持一致
  3. 及时处理事件流中的错误
  4. 在适当时机释放资源