如何在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
相关产品推荐
相关产品推荐

