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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 05:07:39