Flutter应用如何自动检测语音结束并触发API调用?
Flutter语音输入自动检测停止录制方案
核心思路
通过实时监测录制音频的音量强度,当音量低于设定阈值且持续超过指定时长时,判定用户已停止说话,自动触发停止录制并上传音频至API。
依赖选择
使用以下两个核心包实现功能:
record:轻量稳定的音频录制工具,支持实时音量监测audio_session:处理iOS/Android的音频权限与会话配置
在pubspec.yaml中添加依赖:
dependencies: record: ^4.4.0 audio_session: ^0.1.16
完整代码实现
1. 初始化与权限处理
import 'package:flutter/material.dart'; import 'package:record/record.dart'; import 'package:audio_session/audio_session.dart'; import 'dart:io'; import 'package:http/http.dart' as http; class VoiceRecorderPage extends StatefulWidget { const VoiceRecorderPage({super.key}); @override State<VoiceRecorderPage> createState() => _VoiceRecorderPageState(); } class _VoiceRecorderPageState extends State<VoiceRecorderPage> { final AudioRecorder _audioRecorder = AudioRecorder(); bool _isRecording = false; String? _recordPath; DateTime? _lastActiveTime; // 记录最后一次音量高于阈值的时间 final double _volumeThreshold = 0.05; // 音量阈值(根据环境调整) final int _silenceTimeout = 1500; // 静音超时时间(毫秒) @override void initState() { super.initState(); _initAudioSession(); } Future<void> _initAudioSession() async { final session = await AudioSession.instance; await session.configure(const AudioSessionConfiguration( avAudioSessionCategory: AVAudioSessionCategory.playAndRecord, avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.defaultToSpeaker, avAudioSessionMode: AVAudioSessionMode.spokenAudio, androidAudioAttributes: AndroidAudioAttributes( contentType: AndroidAudioContentType.speech, usage: AndroidAudioUsage.voiceCommunication, ), )); }
2. 录制逻辑与自动停止检测
Future<void> _startRecording() async { try { if (await _audioRecorder.hasPermission()) { await _audioRecorder.start( const RecordConfig( encoder: AudioEncoder.aacLc, sampleRate: 16000, bitRate: 16000, ), path: '/storage/emulated/0/voice_recording.m4a', // 自定义存储路径 ); setState(() => _isRecording = true); _lastActiveTime = DateTime.now(); // 实时监听音量变化 _audioRecorder.onAudioProgress.listen((progress) { if (_isRecording) { // 将分贝值转换为0-1范围的音量(需根据设备实际情况调整) final normalizedVolume = (progress.decibels + 60) / 60; if (normalizedVolume > _volumeThreshold) { _lastActiveTime = DateTime.now(); } else { // 检查是否超过静音超时 final silenceDuration = DateTime.now().difference(_lastActiveTime!).inMilliseconds; if (silenceDuration > _silenceTimeout) { _stopRecordingAndUpload(); } } } }); } } catch (e) { debugPrint('录制启动失败: $e'); } } Future<void> _stopRecordingAndUpload() async { if (_isRecording) { _recordPath = await _audioRecorder.stop(); setState(() => _isRecording = false); // 上传音频至API if (_recordPath != null) { await _uploadAudio(File(_recordPath!)); } } }
3. 音频上传至API
Future<void> _uploadAudio(File audioFile) async { try { var request = http.MultipartRequest( 'POST', Uri.parse('https://your-api-endpoint.com/process-voice'), ); request.files.add(await http.MultipartFile.fromPath('audio', audioFile.path)); request.fields['source'] = 'flutter_app'; final response = await request.send(); if (response.statusCode == 200) { debugPrint('音频上传成功'); // 处理API返回结果 } else { debugPrint('上传失败: ${response.statusCode}'); } } catch (e) { debugPrint('上传出错: $e'); } }
4. UI部分
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('语音输入')), body: Center( child: ElevatedButton( style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16), backgroundColor: _isRecording ? Colors.red : Colors.blue, ), onPressed: _isRecording ? null : _startRecording, child: Text( _isRecording ? '录制中...' : '开始语音输入', style: const TextStyle(fontSize: 18), ), ), ), ); } @override void dispose() { _audioRecorder.dispose(); super.dispose(); } }
关键调整点
- 音量阈值:
_volumeThreshold需根据测试环境调整,嘈杂环境可提高至0.1,安静环境可降至0.03 - 静音超时:
_silenceTimeout建议设置在1000-2000毫秒之间,平衡用户正常停顿和真正结束说话的判断 - 分贝转音量:不同设备的分贝范围存在差异,需根据实际测试微调转换逻辑
- 权限配置:Android需在
AndroidManifest.xml添加RECORD_AUDIO权限,iOS需在Info.plist添加NSMicrophoneUsageDescription
内容的提问来源于stack exchange,提问作者Dhruv Sinha
相关产品推荐
相关产品推荐

