You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 18:22:38