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

如何在Flutter的Stack中用CustomPainter为指定图层添加暗色遮罩

解决方案:仅加深指定CustomPainter区域且不影响下层

要实现只加深MiddlePaint、不影响BottomPaint的效果,核心是隔离MiddlePaint和TopPaint的绘制上下文,避免混合模式穿透到底层图层。以下是两种可行方案:


方案一:合并Middle与加深蒙版到同一个CustomPainter

将MiddlePaint的绘制逻辑和局部加深逻辑合并到一个CustomPainter中,通过canvas.saveLayer()限定加深效果的作用范围,确保只影响当前Painter内的内容。

修改后的代码示例

// 替换原MyHomePage中的Stack部分
Stack(
  children: [
    CustomPaint(
      painter: BottomPaint(),
      child: const SizedBox.expand(),
    ),
    CustomPaint(
      painter: MiddleWithDarkenPaint(), // 合并后的Painter
      child: const SizedBox.expand(),
    ),
  ],
),

// 新增合并后的CustomPainter
class MiddleWithDarkenPaint extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // 1. 绘制原MiddlePaint的蓝色路径
    final path = Path()
      ..moveTo(0, size.height)
      ..lineTo(size.width / 2, size.height / 2)
      ..lineTo(size.width, size.height)
      ..lineTo(0, size.height);
    canvas.drawPath(
      path,
      Paint()..color = Colors.blue,
    );

    // 2. 保存当前图层,限定加深效果仅作用于后续绘制区域
    final darkenRect = Rect.fromLTWH(0, size.height / 2, size.width, size.height / 2);
    canvas.saveLayer(darkenRect, Paint());

    // 3. 绘制加深蒙版,使用BlendMode.multiply实现自然加深
    canvas.drawRect(
      darkenRect,
      Paint()
        ..color = Colors.black.withOpacity(0.3)
        ..blendMode = BlendMode.multiply,
    );

    // 4. 恢复图层,结束隔离
    canvas.restore();
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

方案二:嵌套CustomPaint隔离绘制上下文

保持三个CustomPaint的结构,但将MiddlePaint和TopPaint嵌套在一起,通过canvas.saveLayer()让TopPaint的混合模式仅作用于父级的MiddlePaint,不会穿透到BottomPaint。

修改后的代码示例

// 替换原MyHomePage中的Stack部分
Stack(
  children: [
    CustomPaint(
      painter: BottomPaint(),
      child: const SizedBox.expand(),
    ),
    // 将Middle和Top嵌套,隔离绘制上下文
    CustomPaint(
      painter: MiddlePaint(),
      child: CustomPaint(
        painter: TopFixedPaint(),
        child: const SizedBox.expand(),
      ),
    ),
  ],
),

// 修改后的TopPaint
class TopFixedPaint extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final fullRect = Offset.zero & size;
    // 保存整个图层,确保混合模式仅作用于父级MiddlePaint
    canvas.saveLayer(fullRect, Paint());

    final numberOfSections = BlendMode.values.length;
    final sectionWidth = size.width / numberOfSections;
    final sectionHeight = size.height / 2;

    for (var i = 0; i < BlendMode.values.length; i++) {
      final blendMode = BlendMode.values[i];
      final x = i * sectionWidth;
      final y = size.height - sectionHeight;
      final rect = Rect.fromLTWH(x, y, sectionWidth, sectionHeight);
      canvas.drawRect(
        rect,
        Paint()
          ..color = Colors.black.withOpacity(0.3)
          ..blendMode = blendMode,
      );

      printName(blendMode, canvas, x, sectionWidth, y, sectionHeight);
    }

    canvas.restore();
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

核心原理

默认情况下,Stack中的每个CustomPaint都会直接绘制到全局画布上,导致上层的混合模式会影响所有下层图层。通过canvas.saveLayer()可以创建一个独立的绘制图层:

  • 方案一中,先绘制MiddlePaint的内容,再通过saveLayer()限定加深区域,确保加深效果只作用于刚绘制的蓝色路径。
  • 方案二中,嵌套结构让TopPaint的绘制上下文仅继承自MiddlePaint,再配合saveLayer()隔离全局画布,避免混合模式穿透到BottomPaint。

推荐使用BlendMode.multiply实现加深效果,它能让深色蒙版与下方颜色自然混合,比半透明黑色的叠加效果更符合“加深”的视觉需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 17:05:58