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

如何在Flutter应用中实现通话录制功能?求解决方案与技术指导

Flutter通话录制功能实现方案

一、可用的Flutter插件

目前Flutter生态中有几款可直接复用的通话录制插件,能快速完成功能集成:

  • flutter_call_recorder:支持Android和iOS双平台,封装了原生录制逻辑,配置好权限后调用API即可启动/停止录制。
  • call_recorder:跨平台基础录制工具,支持自定义录制文件格式和存储路径。

使用插件前必须配置对应权限:

  • Android:在AndroidManifest.xml中添加RECORD_AUDIO、READ_PHONE_STATE权限;Android 10及以上需额外申请CALL_RECORDING权限(部分国产厂商有更严格的权限管控,需单独适配)。
  • iOS:在Info.plist中添加NSMicrophoneUsageDescription权限说明,iOS通话录制依赖CallKit框架,部分场景需向苹果申请特殊权限。

二、不依赖第三方插件的实现方式

如果不想依赖第三方插件,可通过Flutter原生通道(MethodChannel) 自行对接原生代码实现:

Flutter端代码示例

import 'package:flutter/services.dart';

class CustomCallRecorder {
  static const MethodChannel _channel = MethodChannel('custom_call_recorder');

  // 启动录制,返回是否成功
  static Future<bool> startRecording() async {
    try {
      return await _channel.invokeMethod<bool>('startRecording') ?? false;
    } on PlatformException catch (e) {
      print("启动录制失败: ${e.details}");
      return false;
    }
  }

  // 停止录制,返回录制文件路径
  static Future<String?> stopRecording() async {
    try {
      return await _channel.invokeMethod<String>('stopRecording');
    } on PlatformException catch (e) {
      print("停止录制失败: ${e.details}");
      return null;
    }
  }
}

Android端原生代码(Kotlin)

在MainActivity.kt中实现MethodChannel逻辑处理:

import android.media.MediaRecorder
import android.os.Environment
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.io.File

class MainActivity : FlutterActivity() {
    private val CHANNEL = "custom_call_recorder"
    private var recorder: MediaRecorder? = null
    private var recordingFile: File? = null

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            when (call.method) {
                "startRecording" -> {
                    val success = startRecording()
                    result.success(success)
                }
                "stopRecording" -> {
                    val filePath = stopRecording()
                    result.success(filePath)
                }
                else -> result.notImplemented()
            }
        }
    }

    private fun startRecording(): Boolean {
        return try {
            recordingFile = File(
                getExternalFilesDir(Environment.DIRECTORY_MUSIC),
                "call_rec_${System.currentTimeMillis()}.mp4"
            )
            recorder = MediaRecorder().apply {
                setAudioSource(MediaRecorder.AudioSource.VOICE_CALL)
                setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
                setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
                setOutputFile(recordingFile?.absolutePath)
                prepare()
                start()
            }
            true
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }

    private fun stopRecording(): String? {
        return try {
            recorder?.stop()
            recorder?.release()
            recorder = null
            recordingFile?.absolutePath
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }
}

iOS端原生代码(Swift)

在AppDelegate.swift中实现录制逻辑:

import UIKit
import Flutter
import AVFoundation

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    private let CHANNEL = "custom_call_recorder"
    var recorder: AVAudioRecorder?

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
        let channel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: controller.binaryMessenger)
        channel.setMethodCallHandler({ [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
            guard let self = self else { return }
            switch call.method {
            case "startRecording":
                self.startRecording(result: result)
            case "stopRecording":
                self.stopRecording(result: result)
            default:
                result(FlutterMethodNotImplemented)
            }
        })
        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    private func startRecording(result: FlutterResult) {
        let session = AVAudioSession.sharedInstance()
        do {
            try session.setCategory(.playAndRecord, mode: .default)
            try session.setActive(true)
            
            let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
            let filePath = "\(documentsPath)/call_rec_\(Date().timeIntervalSince1970).m4a"
            let url = URL(fileURLWithPath: filePath)
            
            let settings = [
                AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
                AVSampleRateKey: 44100.0,
                AVNumberOfChannelsKey: 2,
                AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
            ]
            
            recorder = try AVAudioRecorder(url: url, settings: settings)
            recorder?.record()
            result(true)
        } catch {
            result(FlutterError(code: "RECORD_ERROR", message: error.localizedDescription, details: nil))
        }
    }

    private func stopRecording(result: FlutterResult) {
        recorder?.stop()
        recorder = nil
        let session = AVAudioSession.sharedInstance()
        do {
            try session.setActive(false)
            result(nil) // 可修改为返回文件路径,此处简化处理
        } catch {
            result(FlutterError(code: "STOP_ERROR", message: error.localizedDescription, details: nil))
        }
    }
}

三、替代方案(生态内不可行时)

  1. 引导使用系统录制功能:Android 10+和iOS均自带通话录制功能,可通过应用引导用户手动开启,规避权限和兼容性问题。
  2. 集成第三方通话服务:如Twilio、Agora等,这类服务提供自带的通话录制API,无需处理原生权限限制,但需依赖其SDK重构通话模块。

四、关键注意事项

  • Android部分厂商(如小米、华为)对通话录制有额外限制,需覆盖主流机型测试兼容性。
  • iOS通话录制需配合CallKit框架,App Store审核时需明确说明录制用途,确保符合隐私法规。
  • 所有录制行为必须提前告知用户并获得明确同意,符合GDPR、国内隐私法等合规要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 22:44:51