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

如何在Flutter中实现这类自拍相机遮罩效果?

自拍相机遮罩效果

我想要在Flutter中制作这类相机遮罩用于自拍,让面部区域保持清晰聚焦,其余区域呈现透明效果,请问该如何实现?


Flutter实现自拍面部清晰遮罩方案

1. 引入必要依赖

要实现这个效果,需要用到相机预览和面部检测的插件,在pubspec.yaml中添加:

dependencies:
  camera: ^0.10.5+5
  google_mlkit_face_detection: ^0.10.0

执行flutter pub get完成依赖安装。

2. 初始化相机与实时面部检测

先获取前置相机并初始化控制器,然后监听预览帧,实时检测面部位置:

import 'package:camera/camera.dart';
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';

late CameraController _cameraController;
final _faceDetector = FaceDetector(
  options: FaceDetectorOptions(
    enableContours: false,
    enableClassification: false,
  ),
);
Rect? _faceRect;

// 初始化相机
Future<void> initCamera() async {
  final cameras = await availableCameras();
  final frontCamera = cameras.firstWhere((cam) => cam.lensDirection == CameraLensDirection.front);
  _cameraController = CameraController(frontCamera, ResolutionPreset.medium);
  await _cameraController.initialize();
  
  // 监听预览帧,检测面部
  _cameraController.startImageStream((image) async {
    final inputImage = InputImage.fromBytes(
      bytes: image.planes.first.bytes,
      metadata: InputImageMetadata(
        size: Size(image.width.toDouble(), image.height.toDouble()),
        rotation: InputImageRotationValue.fromRawValue(frontCamera.sensorOrientation)!,
        format: InputImageFormatValue.fromRawValue(image.format.raw)!,
        bytesPerRow: image.planes.first.bytesPerRow,
      ),
    );
    final faces = await _faceDetector.processImage(inputImage);
    if (faces.isNotEmpty) {
      setState(() => _faceRect = faces.first.boundingBox);
    }
  });
}

3. 自定义遮罩绘制

创建CustomPainter来绘制全屏半透明遮罩,并挖空面部区域:

class FaceMaskPainter extends CustomPainter {
  final Rect? faceRect;
  final Size previewSize;
  final Size screenSize;

  FaceMaskPainter({this.faceRect, required this.previewSize, required this.screenSize});

  @override
  void paint(Canvas canvas, Size size) {
    // 绘制全屏半透明遮罩
    final maskPaint = Paint()
      ..color = Colors.black.withOpacity(0.7)
      ..style = PaintingStyle.fill;
    canvas.drawRect(Offset.zero & size, maskPaint);

    if (faceRect != null) {
      // 转换面部坐标适配屏幕尺寸
      final scaleX = screenSize.width / previewSize.width;
      final scaleY = screenSize.height / previewSize.height;
      final scaledFace = Rect.fromLTWH(
        faceRect!.left * scaleX,
        faceRect!.top * scaleY,
        faceRect!.width * scaleX,
        faceRect!.height * scaleY,
      );
      // 挖空面部区域,露出下方相机预览
      canvas.drawRect(scaledFace, Paint()..blendMode = BlendMode.clear);
    }
  }

  @override
  bool shouldRepaint(FaceMaskPainter oldDelegate) => oldDelegate.faceRect != faceRect;
}

4. 组合组件展示

把相机预览和遮罩层叠加,放在页面中:

@override
Widget build(BuildContext context) {
  final screenSize = MediaQuery.of(context).size;
  return Scaffold(
    body: Stack(
      children: [
        CameraPreview(_cameraController),
        CustomPaint(
          size: screenSize,
          painter: FaceMaskPainter(
            faceRect: _faceRect,
            previewSize: _cameraController.value.previewSize!,
            screenSize: screenSize,
          ),
        ),
      ],
    ),
  );
}

额外注意事项

  • Android端要在AndroidManifest.xml中添加相机权限:<uses-permission android:name="android.permission.CAMERA" />
  • iOS端在Info.plist中添加:<key>NSCameraUsageDescription</key><string>需要相机权限进行自拍</string>
  • 可以根据需求调整遮罩透明度、面部区域的圆角(把drawRect换成drawRRect即可)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 10:33:27