如何在Flutter的Flyer Chat中实现类iMessage/WhatsApp的语音消息功能?
实现Flyer Chat的语音消息功能(flutter_chat_ui + firebase_chat_core)
1. 添加麦克风按钮到输入工具栏
flutter_chat_ui的InputToolbar组件支持通过customButtons参数自定义输入框右侧按钮,直接在这里添加麦克风按钮触发录音:
Chat( // 保留原有聊天配置(如messages、onMessageTap等) inputToolbar: InputToolbar( customButtons: [ IconButton( icon: const Icon(Icons.mic), onPressed: _toggleRecording, ), ], onSendPressed: _handleTextSend, // 保留原有文本发送逻辑 ), )
2. 实现录音核心逻辑
用record包处理录音功能,先在pubspec.yaml加依赖:
dependencies: record: ^4.4.0
封装录音工具类:
import 'package:record/record.dart'; import 'package:path_provider/path_provider.dart'; class VoiceRecorder { final AudioRecorder _recorder = AudioRecorder(); String? _recordPath; bool _isRecording = false; int _recordDuration = 0; Timer? _durationTimer; Future<void> start() async { final hasPermission = await _recorder.hasPermission(); if (!hasPermission) return; final tempDir = await getTemporaryDirectory(); _recordPath = '${tempDir.path}/voice_${DateTime.now().millisecondsSinceEpoch}.m4a'; await _recorder.start( const RecordConfig(encoder: AudioEncoder.aacLc), path: _recordPath, ); _isRecording = true; // 计时更新录音时长 _durationTimer = Timer.periodic(const Duration(milliseconds: 100), (timer) { _recordDuration += 100; }); } Future<Map<String, dynamic>?> stop() async { if (!_isRecording) return null; _durationTimer?.cancel(); await _recorder.stop(); _isRecording = false; final result = { 'path': _recordPath, 'duration': _recordDuration, }; _recordDuration = 0; return result; } bool get isRecording => _isRecording; }
在页面中初始化并实现切换录音的方法:
final VoiceRecorder _voiceRecorder = VoiceRecorder(); void _toggleRecording() async { if (_voiceRecorder.isRecording) { final recordResult = await _voiceRecorder.stop(); if (recordResult != null) { await _sendVoiceMessage( recordResult['path'] as String, recordResult['duration'] as int, ); } } else { await _voiceRecorder.start(); } }
3. 上传录音到Firebase并发送语音消息
把本地录音文件上传到Firebase Storage,拿到下载链接后,通过firebase_chat_core发送语音类型消息:
import 'package:firebase_storage/firebase_storage.dart'; import 'package:firebase_chat_core/firebase_chat_core.dart'; import 'package:flutter_chat_types/flutter_chat_types.dart' as types; import 'package:uuid/uuid.dart'; Future<void> _sendVoiceMessage(String localPath, int duration) async { // 上传到Storage final storageRef = FirebaseStorage.instance.ref().child( 'voice_messages/${DateTime.now().millisecondsSinceEpoch}.m4a', ); await storageRef.putFile(File(localPath)); final downloadUrl = await storageRef.getDownloadURL(); // 构造语音消息 final voiceMessage = types.Message( author: types.User(id: FirebaseChatCore.instance.currentUser!.id), createdAt: DateTime.now().millisecondsSinceEpoch, id: const Uuid().v4(), // 需要添加uuid依赖:uuid ^4.0.0 type: types.MessageType.audio, uri: downloadUrl, metadata: {'duration': duration}, // 存录音时长,用于UI显示 ); // 发送到Firebase Chat await FirebaseChatCore.instance.sendMessage( voiceMessage, roomId: '你的聊天房间ID', ); }
4. 配置AudioMessageBuilder渲染语音消息
在Chat组件的messageBuilder中,判断消息类型为音频时,使用AudioMessageBuilder渲染:
Chat( // 其他配置... messageBuilder: (context, message) { if (message.type == types.MessageType.audio) { return AudioMessageBuilder( message: message, isCurrentUser: message.author.id == FirebaseChatCore.instance.currentUser!.id, onPlayPressed: (audioMsg) => _playAudio(audioMsg.uri!), onPausePressed: (audioMsg) => _pauseAudio(), duration: Duration(milliseconds: message.metadata?['duration'] ?? 0), ); } // 其他消息类型用默认渲染 return DefaultMessageBuilder(message: message); }, )
5. 补充音频播放逻辑
用audioplayers包实现播放功能,加依赖:
dependencies: audioplayers: ^4.0.1
封装播放工具:
import 'package:audioplayers/audioplayers.dart'; final AudioPlayer _audioPlayer = AudioPlayer(); void _playAudio(String url) async { await _audioPlayer.play(UrlSource(url)); } void _pauseAudio() async { await _audioPlayer.pause(); }
权限配置
- Android:在
AndroidManifest.xml添加权限
<uses-permission android:name="android.permission.RECORD_AUDIO" />
- iOS:在
Info.plist添加权限描述
<key>NSMicrophoneUsageDescription</key> <string>需要使用麦克风录制语音消息</string>
内容的提问来源于stack exchange,提问作者Clermoe
相关产品推荐
相关产品推荐

