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

Flutter中录制音频时实时显示声波的实现方案求助

Flutter 录制音频+实时声波效果实现方案

核心依赖组合

不用找一站式专属包,用两个成熟包搭配就能实现需求:

  • audio_recorder2:负责音频录制,支持实时获取音频振幅数据
  • flutter_waveforms:专门用于绘制声波/波形图,支持自定义样式

实现步骤

1. 添加依赖到pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  audio_recorder2: ^2.0.0
  flutter_waveforms: ^1.0.0

执行flutter pub get完成依赖安装。

2. 权限配置

  • Android:在AndroidManifest.xml中添加权限
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • iOS:在Info.plist中添加权限描述
<key>NSMicrophoneUsageDescription</key>
<string>需要访问麦克风进行音频录制</string>

3. 核心代码实现

import 'package:flutter/material.dart';
import 'package:audio_recorder2/audio_recorder2.dart';
import 'package:flutter_waveforms/flutter_waveforms.dart';

class AudioRecorderWithWaveform extends StatefulWidget {
  const AudioRecorderWithWaveform({super.key});

  @override
  State<AudioRecorderWithWaveform> createState() => _AudioRecorderWithWaveformState();
}

class _AudioRecorderWithWaveformState extends State<AudioRecorderWithWaveform> {
  bool _isRecording = false;
  List<double> _waveformData = [];
  late RecorderController _recorderController;

  @override
  void initState() {
    super.initState();
    _recorderController = RecorderController()
      ..onAmplitudeChanged = (amplitude) {
        if (_isRecording) {
          setState(() {
            // 将振幅归一化到0-1范围,适配波形图绘制
            _waveformData.add(amplitude.normalized);
            // 限制数据长度,避免内存占用过高
            if (_waveformData.length > 100) {
              _waveformData.removeAt(0);
            }
          });
        }
      };
  }

  @override
  void dispose() {
    _recorderController.dispose();
    super.dispose();
  }

  Future<void> _toggleRecording() async {
    if (_isRecording) {
      await _recorderController.stop();
      setState(() => _isRecording = false);
    } else {
      await _recorderController.start();
      setState(() {
        _isRecording = true;
        _waveformData.clear();
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 实时波形图区域
            Container(
              height: 150,
              width: double.infinity,
              padding: const EdgeInsets.symmetric(horizontal: 20),
              child: WaveformWidget(
                data: _waveformData,
                style: WaveformStyle(
                  waveColor: Colors.blue,
                  backgroundColor: Colors.grey[200],
                  showMiddleLine: false,
                ),
              ),
            ),
            const SizedBox(height: 40),
            // 录制控制按钮
            ElevatedButton(
              onPressed: _toggleRecording,
              style: ElevatedButton.styleFrom(
                shape: const CircleBorder(),
                padding: const EdgeInsets.all(20),
              ),
              child: Icon(_isRecording ? Icons.stop : Icons.mic),
            ),
          ],
        ),
      ),
    );
  }
}

4. 自定义波形样式

如果默认样式不符合需求,可通过WaveformStyle调整:

  • 修改waveColor设置波形颜色
  • 调整waveThickness改变线条粗细
  • 设置maxHeight控制波形最大高度
  • 开启showMiddleLine显示中线

备选方案:自定义绘制波形

如果flutter_waveforms不满足需求,可通过CustomPaint手动绘制:

class CustomWaveform extends StatelessWidget {
  final List<double> data;
  const CustomWaveform({super.key, required this.data});

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: WaveformPainter(data: data),
      size: const Size(double.infinity, 150),
    );
  }
}

class WaveformPainter extends CustomPainter {
  final List<double> data;
  WaveformPainter({required this.data});

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = Colors.blue;
    final step = size.width / data.length;
    for (int i = 0; i < data.length; i++) {
      final height = data[i] * size.height;
      canvas.drawRect(
        Rect.fromLTWH(
          i * step,
          (size.height - height) / 2,
          step - 2,
          height,
        ),
        paint,
      );
    }
  }

  @override
  bool shouldRepaint(covariant WaveformPainter oldDelegate) {
    return oldDelegate.data != data;
  }
}

内容的提问来源于stack exchange,提问作者VolkanUstekidag

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 13:45:30