Flutter使用camera package时,如何在录制视频时抓拍照片?
嘿,这个需求我之前帮不少开发者解决过!Flutter官方的camera包确实没有直接提供“录视频时实时抓拍”的API,但我们可以通过捕获预览帧或者事后从视频提取帧的方式来实现,下面给你两种实用的方案:
方案一:实时抓拍(从预览帧生成图片)
这个方案是在录视频的同时,监听相机的预览流,当用户触发抓拍时,把当前的预览帧转换成图片保存,完全实时。
实现步骤:
- 先在
pubspec.yaml中添加image依赖(用于转换相机帧格式):
dependencies: flutter: sdk: flutter camera: ^0.10.0+1 image: ^4.0.17 path_provider: ^2.0.15
- 核心代码实现:
import 'package:camera/camera.dart'; import 'package:image/image.dart' as img; import 'package:path_provider/path_provider.dart'; import 'dart:io'; import 'dart:typed_data'; class CameraScreen extends StatefulWidget { @override _CameraScreenState createState() => _CameraScreenState(); } class _CameraScreenState extends State<CameraScreen> { CameraController? _controller; bool _isRecording = false; bool _shouldCapture = false; XFile? _capturedImage; @override void initState() { super.initState(); _initCamera(); } Future<void> _initCamera() async { final cameras = await availableCameras(); _controller = CameraController(cameras[0], ResolutionPreset.high); await _controller!.initialize(); setState(() {}); } // 开始录制视频 Future<void> startRecording() async { final controller = _controller!; if (!controller.value.isInitialized || controller.value.isRecordingVideo) return; try { setState(() => _isRecording = true); await controller.startVideoRecording(); // 启动预览帧流,用于抓拍 controller.startImageStream((CameraImage image) async { if (_shouldCapture) { _capturedImage = await _convertFrameToImage(image); setState(() => _shouldCapture = false); // 这里可以添加保存到相册或UI更新的逻辑 } }); } on CameraException catch (e) { print('录制出错: ${e.description}'); setState(() => _isRecording = false); } } // 结束录制视频 Future<void> stopRecording() async { final controller = _controller!; if (!controller.value.isRecordingVideo) return; try { await controller.stopVideoRecording(); setState(() => _isRecording = false); controller.stopImageStream(); // 停止帧流监听 } on CameraException catch (e) { print('停止录制出错: ${e.description}'); } } // 抓拍按钮触发方法 void captureDuringRecording() { if (_isRecording) setState(() => _shouldCapture = true); } // 将CameraImage转换为可保存的XFile Future<XFile> _convertFrameToImage(CameraImage image) async { img.Image convertedImage; // 处理不同设备的图像格式,YUV420是通用格式 if (image.format.group == ImageFormatGroup.yuv420) { convertedImage = img.convertYUV420(image); } else if (image.format.group == ImageFormatGroup.bgra8888) { convertedImage = img.convertBGRA8888(image); } else { throw Exception('不支持的图像格式'); } final jpegBytes = img.encodeJpg(convertedImage); final tempDir = await getTemporaryDirectory(); final file = File('${tempDir.path}/抓拍照片_${DateTime.now().millisecondsSinceEpoch}.jpg'); await file.writeAsBytes(jpegBytes); return XFile(file.path); } @override void dispose() { _controller?.dispose(); super.dispose(); } // 省略UI部分代码,你可以根据自己的布局添加录制、抓拍按钮 }
方案二:事后从视频截取帧(非实时)
如果你的场景对实时性要求不高,也可以在视频录制完成后,从已保存的视频文件里提取指定帧作为抓拍的照片,这种方式更省资源。
实现步骤:
- 添加
video_player依赖到pubspec.yaml:
dependencies: video_player: ^2.7.0
- 核心代码:
import 'package:video_player/video_player.dart'; // 从视频文件中截取第一帧作为抓拍照片 Future<XFile> captureFrameFromVideo(XFile videoFile) async { final controller = VideoPlayerController.file(File(videoFile.path)); await controller.initialize(); await controller.pause(); // 停在第一帧 final capturedImage = await controller.takePicture(); await controller.dispose(); return capturedImage; } // 使用示例:录制完成后调用 Future<void> onRecordingStopped(XFile videoFile) async { final capturedPhoto = await captureFrameFromVideo(videoFile); // 保存capturedPhoto到相册或其他位置 }
注意事项
- 务必确保已经申请了相机、存储、麦克风权限(录视频必须要有麦克风权限)
- 高分辨率下开启预览帧流会增加CPU负载,可以根据需求调整
ResolutionPreset为中等或低分辨率 - 测试不同设备时,优先适配YUV420格式(大部分安卓和iOS设备都支持)
内容的提问来源于stack exchange,提问作者dinesh.rajbhar




