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

如何实现Flutter与Swift双向数据传递及Flutter按钮触发Swift拍照

Flutter与Swift自定义相机:双向通信及拍照触发实现方案

一、开发环境与当前进展

  • Flutter版本:flutter doctor -v截图
  • Xcode版本:XCode版本截图
  • 已完成自定义相机界面搭建

二、需求目标

  1. 实现Flutter与Swift的双向数据传递
  2. 点击Flutter端按钮触发Swift端执行拍照操作

三、解决方案

(一)双向数据传递实现(基于MethodChannel)

Flutter与iOS原生的双向通信核心依赖MethodChannel,既可以从Flutter调用Swift方法,也可以从Swift主动触发Flutter逻辑。

1. Swift端配置MethodChannel

修改NativeView的初始化方法,注册Channel并处理Flutter调用,同时支持主动向Flutter发送消息:

// NativeView.swift
init(
    frame: CGRect,
    viewIdentifier viewId: Int64,
    arguments args: Any?,
    binaryMessenger messenger: FlutterBinaryMessenger?
) {
    returnView = UIView()
    super.init()
    createNativeView(view: returnView!, args: args)
    
    // 注册MethodChannel
    guard let messenger = messenger else { return }
    let channel = FlutterMethodChannel(name: "com.vrin.methodchannel/cameraButton", binaryMessenger: messenger)
    
    // 处理Flutter调用的方法
    channel.setMethodCallHandler { [weak self] call, result in
        switch call.method {
        case "takePhoto":
            self?.takePhoto(completion: { imageData in
                imageData != nil ? result(imageData!.base64EncodedString()) : result(FlutterError(code: "FAIL", message: "拍照失败", details: nil))
            })
        case "requestNativeData":
            result("来自Swift的测试数据")
        default:
            result(FlutterMethodNotImplemented)
        }
    }
    
    // Swift主动调用Flutter方法示例
    channel.invokeMethod("cameraReady", arguments: "相机初始化完成")
}

2. Flutter端监听与调用

在Dart代码中添加Channel监听,处理Swift主动发送的消息,并实现调用Swift方法的逻辑:

// .dart文件
@override
void initState() {
    super.initState();
    // 监听Swift主动触发的方法
    platformChannel.setMethodCallHandler((call) async {
        switch (call.method) {
            case "cameraReady":
                print("收到Swift消息:${call.arguments}");
                break;
            default:
                throw MissingPluginException();
        }
    });
}

// 按钮点击逻辑中调用Swift方法
GestureDetector(
    onTap: () async {
        print("pressed");
        try {
            // 调用Swift拍照方法
            String? photoBase64 = await platformChannel.invokeMethod("takePhoto");
            if (photoBase64 != null) {
                Uint8List photoBytes = base64Decode(photoBase64);
                setState(() {
                    nativePhoto = Image.memory(photoBytes);
                });
            }
        } on PlatformException catch (e) {
            print("拍照失败:${e.message}");
        }
    },
    // ... 按钮UI代码 ...
)

(二)Flutter按钮触发Swift拍照

1. Swift端完善拍照逻辑

让NativeView遵循AVCapturePhotoCaptureDelegate协议,实现拍照与结果回调:

// NativeView.swift
class NativeView: NSObject, FlutterPlatformView, AVCapturePhotoCaptureDelegate {
    private var photoCompletion: ((Data?) -> Void)?
    
    // 添加拍照方法
    func takePhoto(completion: @escaping (Data?) -> Void) {
        guard let photoOutput = photoOutput, session.isRunning else {
            completion(nil)
            return
        }
        self.photoCompletion = completion
        
        let settings = AVCapturePhotoSettings()
        settings.format = [AVVideoCodecKey: AVVideoCodecType.jpeg]
        if photoOutput.isDepthDataDeliverySupported {
            settings.isDepthDataDeliveryEnabled = true
        }
        photoOutput.capturePhoto(with: settings, delegate: self)
    }
    
    // 拍照完成代理方法
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        defer { photoCompletion = nil }
        if let error = error {
            print("拍照错误:\(error)")
            photoCompletion?(nil)
            return
        }
        photoCompletion?(photo.fileDataRepresentation())
    }
}

2. 修复相机Session重复输入问题

当前代码中setupAVCapture和beginSession重复添加相机输入,会导致错误,修改如下:

// setupAVCapture方法
func setupAVCapture(){
    session.sessionPreset = AVCaptureSession.Preset.photo
    let device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera, .builtInWideAngleCamera], mediaType: .video, position: .back).devices
    captureDevice = device.first
    do {
        let input = try AVCaptureDeviceInput(device: captureDevice!)
        if !session.inputs.contains(input) {
            session.addInput(input)
        }
        photoOutput = AVCapturePhotoOutput()
        photoOutput?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
        if session.canAddOutput(photoOutput!) {
            session.addOutput(photoOutput!)
            photoOutput!.isDepthDataDeliveryEnabled = photoOutput!.isDepthDataDeliverySupported
        }
    } catch {
        print(error)
    }
    beginSession()
}

// beginSession方法
func beginSession(){
    do {
        videoDataOutput = AVCaptureVideoDataOutput()
        videoDataOutput.alwaysDiscardsLateVideoFrames = true
        videoDataOutputQueue = DispatchQueue(label: "VideoDataOutputQueue")
        videoDataOutput.setSampleBufferDelegate(self, queue: videoDataOutputQueue)
        
        if session.canAddOutput(videoDataOutput){
            session.addOutput(videoDataOutput)
        }
        videoDataOutput.connection(with: .video)?.isEnabled = true
        
        previewLayer = AVCaptureVideoPreviewLayer(session: session)
        previewLayer.videoGravity = .resizeAspect
        let rootLayer = previewView.layer
        rootLayer.masksToBounds = true
        previewLayer.frame = rootLayer.bounds
        rootLayer.addSublayer(previewLayer)
        
        session.startRunning()
    } catch let error as NSError {
        print("Session启动错误:\(error.localizedDescription)")
    }
}

(三)必要配置

在Info.plist中添加相机权限描述:

<key>NSCameraUsageDescription</key>
<string>需要使用相机进行拍照</string>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 13:41:00