如何实现Flutter与Swift双向数据传递及Flutter按钮触发Swift拍照
Flutter与Swift自定义相机:双向通信及拍照触发实现方案
一、开发环境与当前进展
- Flutter版本:flutter doctor -v截图
- Xcode版本:XCode版本截图
- 已完成自定义相机界面搭建
二、需求目标
- 实现Flutter与Swift的双向数据传递
- 点击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
相关产品推荐
相关产品推荐

