一、项目需求分析与交互设计
微信语音消息发送的核心交互包含三个阶段:长按录音、滑动取消、松开发送。开发者需要重点解决以下技术难点:
- 按钮状态机管理(正常/录音中/取消)
- 录音时长控制(1秒触发/60秒限制)
- 滑动取消的视觉反馈
- 录音波形动态渲染
通过Flutter的GestureDetector和AnimationController组合,可以精准控制各状态转换。建议采用状态模式设计按钮行为,将不同手势操作映射到具体状态。
二、语音按钮核心实现
2.1 基础按钮结构
class VoiceButton extends StatefulWidget {@override_VoiceButtonState createState() => _VoiceButtonState();}class _VoiceButtonState extends State<VoiceButton> {VoiceButtonStatus _status = VoiceButtonStatus.normal;final _animationController = AnimationController(duration: const Duration(milliseconds: 300),vsync: this,);@overridevoid dispose() {_animationController.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return GestureDetector(onLongPressStart: _handleLongPressStart,onLongPressMoveUpdate: _handleMoveUpdate,onLongPressEnd: _handleLongPressEnd,child: AnimatedBuilder(animation: _animationController,builder: (context, child) {return Container(width: 70 + _animationController.value * 10,height: 70 + _animationController.value * 10,decoration: BoxDecoration(shape: BoxShape.circle,color: _status == VoiceButtonStatus.cancel? Colors.red[400]: Colors.green[400],),child: Center(child: Icon(_status == VoiceButtonStatus.recording? Icons.mic: Icons.mic_off,size: 30 + _animationController.value * 5,),),);},),);}}
2.2 状态管理实现
采用枚举类型管理三种核心状态:
enum VoiceButtonStatus {normal, // 初始状态recording, // 录音中cancel // 滑动取消}
通过AnimationController控制按钮尺寸变化:
- 长按时触发放大动画(0.8→1.2倍)
- 取消时触发红色闪烁效果
- 发送后恢复原始尺寸
三、录音功能集成
3.1 录音插件选择
推荐使用flutter_sound插件,其优势包括:
- 支持iOS/Android双平台
- 提供实时音频流回调
- 内置录音权限处理
配置步骤:
-
添加依赖:
dependencies:flutter_sound: ^9.2.13
-
初始化录音器:
```dart
final _recorder = FlutterSoundRecorder();
Future
const codec = Codec.aacADTS;
await _recorder.openRecorder();
await _recorder.setSubscriptionDuration(
const Duration(milliseconds: 100),
);
}
## 3.2 录音控制逻辑```dartvoid _startRecording() async {if (_status != VoiceButtonStatus.recording) {setState(() {_status = VoiceButtonStatus.recording;_animationController.forward();});await _recorder.startRecorder(toFile: 'audio_${DateTime.now().millisecondsSinceEpoch}.aac',codec: Codec.aacADTS,);// 启动录音时长监控_recordingTimer = Timer.periodic(const Duration(seconds: 1),(timer) {if (_recordingDuration >= 60) {_stopRecording(isCancelled: false);} else {_recordingDuration++;}},);}}void _stopRecording({required bool isCancelled}) async {_recordingTimer?.cancel();await _recorder.stopRecorder();if (mounted) {setState(() {_status = isCancelled? VoiceButtonStatus.cancel: VoiceButtonStatus.normal;_animationController.reverse();});if (!isCancelled && _recordingDuration >= 1) {// 处理音频文件final audioFile = File('audio_${DateTime.now().millisecondsSinceEpoch}.aac');// 上传或播放逻辑...}}}
四、波形动画实现
4.1 自定义波形组件
class WaveForm extends StatefulWidget {final Stream<List<double>> amplitudeStream;const WaveForm({required this.amplitudeStream, Key? key}) : super(key: key);@override_WaveFormState createState() => _WaveFormState();}class _WaveFormState extends State<WaveForm> {List<double> _amplitudes = [];@overridevoid initState() {super.initState();widget.amplitudeStream.listen((amplitudes) {if (mounted) {setState(() {_amplitudes = amplitudes;});}});}@overrideWidget build(BuildContext context) {return AspectRatio(aspectRatio: 2,child: CustomPaint(painter: WaveFormPainter(_amplitudes),),);}}class WaveFormPainter extends CustomPainter {final List<double> amplitudes;WaveFormPainter(this.amplitudes);@overridevoid paint(Canvas canvas, Size size) {final paint = Paint()..color = Colors.green[300]!..strokeWidth = 2..style = PaintingStyle.stroke;final path = Path();final pointCount = amplitudes.length;final stepX = size.width / (pointCount - 1);for (int i = 0; i < pointCount; i++) {final x = i * stepX;final y = size.height / 2 * (1 - amplitudes[i].clamp(0, 1));if (i == 0) {path.moveTo(x, y);} else {path.lineTo(x, y);}}canvas.drawPath(path, paint);}@overridebool shouldRepaint(covariant CustomPainter oldDelegate) => true;}
4.2 实时数据流处理
通过flutter_sound的onRecorderStateChanged获取实时振幅数据:
_recorder.setSubscriptionDuration(const Duration(milliseconds: 50));final amplitudeStream = _recorder.onRecorderStateChanged?.map((event) => event.dbPeakLevel ?? 0).map((db) => (1 + db / 60).clamp(0, 1).toDouble()).buffer(const Duration(milliseconds: 100)).map((samples) {// 生成平滑的波形数据final windowSize = 30;final padded = List<double>.filled(windowSize, 0) + samples;final result = <double>[];for (var i = 0; i < windowSize; i++) {final windowStart = i;final windowEnd = i + samples.length;final window = padded.sublist(windowStart, windowEnd);result.add(window.reduce((a, b) => a + b) / window.length);}return result;});
五、完整页面集成
5.1 页面布局设计
class VoiceMessagePage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('语音消息')),body: Column(children: [Expanded(child: Center(child: Container(width: 200,height: 200,decoration: BoxDecoration(border: Border.all(color: Colors.grey[300]!),borderRadius: BorderRadius.circular(10),),child: const WaveForm(amplitudeStream: Stream.empty()),),),),const Padding(padding: EdgeInsets.all(20),child: VoiceButton(),),const SizedBox(height: 20),Text('滑动可取消发送', style: TextStyle(color: Colors.grey)),],),);}}
5.2 状态同步处理
使用ValueNotifier实现按钮状态与页面UI的同步:
class VoiceButtonController extends ValueNotifier<VoiceButtonStatus> {VoiceButtonController() : super(VoiceButtonStatus.normal);void startRecording() => value = VoiceButtonStatus.recording;void cancelRecording() => value = VoiceButtonStatus.cancel;void reset() => value = VoiceButtonStatus.normal;}// 在页面中使用final controller = VoiceButtonController();ValueListenableBuilder(valueListenable: controller,builder: (context, status, child) {return AnimatedContainer(duration: const Duration(milliseconds: 300),color: status == VoiceButtonStatus.cancel? Colors.red: Colors.green,// 其他UI元素...);},)
六、性能优化建议
-
录音内存管理:
- 及时关闭不再使用的录音器
- 使用
isolate处理耗时的音频编码
-
动画性能优化:
- 对
CustomPaint使用RepaintBoundary - 限制波形数据点数(建议30-60个点)
- 对
-
状态管理优化:
- 使用
Provider或Riverpod管理全局状态 - 避免在
build方法中创建新对象
- 使用
-
平台适配处理:
- iOS需要配置
NSMicrophoneUsageDescription - Android需要动态请求
RECORD_AUDIO权限
- iOS需要配置
七、扩展功能建议
- 语音转文字:集成阿里云/腾讯云语音识别API
- 变声效果:使用
soundpool实现实时音频处理 - 多语言支持:根据系统语言切换提示文本
- 无障碍适配:为按钮添加语音提示功能
通过以上实现方案,开发者可以构建出与微信体验高度一致的语音发送功能。实际开发中建议将录音逻辑封装为独立模块,便于在不同页面复用。对于商业项目,还需考虑添加噪音抑制、回声消除等高级音频处理功能。