Flutter使用canvas.saveLayer实现类PS图层功能的问题排查
Flutter简化版Photoshop图层功能实现问题与解决方案
需求描述
在Flutter中实现简化的图层功能:
- 每个图层内的所有绘图使用同一种固定颜色
- 同一图层内的多次绘图无叠加加深效果
- 不同图层颜色区分明显,上层图层内容可覆盖下层
现有图层结构代码
import 'package:flutter/material.dart'; import 'package:label_up_study/models/sketch.dart'; import 'dart:math'; Color randomColor() { return Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0); } class LayerInfo { List<Sketch> drawLayer; // 存储该图层的所有绘图路径 String label; bool selected; bool visible; Color color; LayerInfo({ required this.drawLayer, required this.label, required this.selected, this.visible = true, required this.color, }); } class Layers { final Map<int, LayerInfo> layers = {}; // 修正语法错误:将=改为=> int get selected => layers.keys.where((key) => layers[key]!.selected).toList().first; void addLayer() { final nextKey = layers.keys.toList().last + 1; layers.updateAll((key, value) { value.selected = false; return value; }); layers[nextKey] = LayerInfo( drawLayer: [], label: 'Layer $nextKey', selected: true, color: randomColor()); } Layers() { layers[1] = LayerInfo( drawLayer: [], label: 'Layer 1', selected: true, color: randomColor()); } }
问题重现
尝试使用canvas.saveLayer实现图层效果,但编写的WholeSketchPainter代码无法达到预期:
- 单图层显示正常,但多图层叠加时颜色互相混合,图层区分不明显
原WholeSketchPainter代码:
class WholeSketchPainter extends CustomPainter { final Layers imageLayer; WholeSketchPainter({required this.imageLayer}); @override void paint(Canvas canvas, Size size) { for (var value in imageLayer.layers.values) { print(value.color); if (value.visible) { canvas.saveLayer(null, Paint()..color = value.color.withOpacity(0.5)); drawSketch(canvas, value.drawLayer, value.color); canvas.restore(); } else { // nothing } } } void drawSketch(Canvas canvas, List<Sketch> sketches, Color layerColor) { Paint strokePaint = Paint() ..style = PaintingStyle.stroke ..color = layerColor.withOpacity(1); Paint fillPaint = Paint() ..style = PaintingStyle.fill ..color = layerColor.withOpacity(1); for (int i = 0; i < sketches.length; i++) { final offsets = sketches[i].points; if (offsets.isEmpty) { return; } final path = Path(); final firstOffset = offsets.first; final lastOffset = offsets.last; path.moveTo(firstOffset.dx, firstOffset.dy); if (sketches[i].sketchType == SketchType.pencil) { for (int i = 1; i < offsets.length - 1; i++) { final p0 = offsets[i]; final p1 = offsets[i + 1]; path.quadraticBezierTo( p0.dx, p0.dy, (p0.dx + p1.dx) / 2, (p0.dy + p1.dy) / 2); } canvas.drawPath(path, fillPaint); } else if (sketches[i].sketchType == SketchType.square) { final rect = Rect.fromPoints(firstOffset, lastOffset); canvas.drawRect(rect, fillPaint); } } } @override bool shouldRepaint(WholeSketchPainter delegate) { return false; } }
问题原因
saveLayer的Paint参数设置了半透明图层颜色,导致当前图层内容与下方图层强制混合,破坏了图层独立的视觉效果- 同一图层内的绘图使用默认的
BlendMode.srcOver,多次绘制同一区域会叠加加深,不符合“同一图层无叠加”的需求
解决方案与修改后代码
修改要点
- 移除
saveLayer中带颜色的Paint参数,恢复正常的图层合成逻辑 - 给绘图画笔添加
BlendMode.src,确保同一图层内的绘制直接覆盖,无叠加加深 - 修正
Layers类中selectedgetter的语法错误 - 优化绘图循环逻辑,避免单个空路径中断整个图层绘制
修改后的WholeSketchPainter代码
class WholeSketchPainter extends CustomPainter { final Layers imageLayer; WholeSketchPainter({required this.imageLayer}); @override void paint(Canvas canvas, Size size) { final layerBounds = Offset.zero & size; for (var value in imageLayer.layers.values) { if (value.visible) { // 保存独立图层,使用默认合成参数 canvas.saveLayer(layerBounds, Paint()); // 绘制当前图层的所有草图 drawSketch(canvas, value.drawLayer, value.color); canvas.restore(); } } } void drawSketch(Canvas canvas, List<Sketch> sketches, Color layerColor) { Paint strokePaint = Paint() ..style = PaintingStyle.stroke ..color = layerColor ..blendMode = BlendMode.src; // 同一图层内绘制直接覆盖,无叠加 Paint fillPaint = Paint() ..style = PaintingStyle.fill ..color = layerColor ..blendMode = BlendMode.src; for (int i = 0; i < sketches.length; i++) { final offsets = sketches[i].points; if (offsets.isEmpty) continue; // 避免单个空路径中断整个图层绘制 final path = Path(); final firstOffset = offsets.first; final lastOffset = offsets.last; path.moveTo(firstOffset.dx, firstOffset.dy); if (sketches[i].sketchType == SketchType.pencil) { for (int j = 1; j < offsets.length - 1; j++) { // 变量名避免与外层冲突 final p0 = offsets[j]; final p1 = offsets[j + 1]; path.quadraticBezierTo( p0.dx, p0.dy, (p0.dx + p1.dx) / 2, (p0.dy + p1.dy) / 2); } canvas.drawPath(path, fillPaint); } else if (sketches[i].sketchType == SketchType.square) { final rect = Rect.fromPoints(firstOffset, lastOffset); canvas.drawRect(rect, fillPaint); } } } @override bool shouldRepaint(WholeSketchPainter delegate) { // 图层内容变化时触发重绘 return delegate.imageLayer.layers != imageLayer.layers; } }
效果验证
- 单图层内多次绘制同一区域,颜色始终保持图层纯色,无叠加加深
- 多图层叠加时,上层图层内容清晰覆盖下层,不同图层颜色区分明显,符合预期
内容的提问来源于stack exchange,提问作者jungeyonoh
相关产品推荐
相关产品推荐

