Flutter中如何为CustomPaint设置适配全屏的同形状背景图
问题解决:CustomPaint背景图适配自定义形状与尺寸
问题现状
使用自定义路径绘制的CustomPaint,设置背景图时仅显示图片原始尺寸,无法适配CustomPaint的尺寸与路径形状,达不到预期效果。
解决方案
核心问题分析
原代码直接调用canvas.drawImage()绘制原始尺寸图片,既未将图片缩放到CustomPaint的尺寸,也未限制图片显示在自定义路径范围内,导致显示异常。
修改方案(二选一即可)
方案1:画布裁剪+适配绘制
修改BackgroundCustomPainter的paint方法,先裁剪画布到自定义路径,再将图片缩放到CustomPaint尺寸绘制:
class BackgroundCustomPainter extends CustomPainter { final ui.Image? myBackground; const BackgroundCustomPainter(this.myBackground); @override void paint(Canvas canvas, Size size) { Path path_0 = Path(); // 保留原路径绘制代码 path_0.moveTo(size.width * 0.01063777, size.height * 0.01464467); path_0.cubicTo(0, size.height * 0.02928933, 0, size.height * 0.05285967, 0, size.height * 0.1000000); path_0.lineTo(0, size.height * 0.9000000); path_0.cubicTo(0, size.height * 0.9471400, 0, size.height * 0.9707100, size.width * 0.01063777, size.height * 0.9853567); path_0.cubicTo( size.width * 0.02127554, size.height, size.width * 0.03839685, size.height, size.width * 0.07263923, size.height); path_0.lineTo(size.width * 0.3588063, size.height); path_0.cubicTo(size.width * 0.3683777, size.height, size.width * 0.3731622, size.height, size.width * 0.3753438, size.height * 0.9962100); path_0.cubicTo( size.width * 0.3775254, size.height * 0.9924200, size.width * 0.3763269, size.height * 0.9854000, size.width * 0.3739274, size.height * 0.9713600); path_0.cubicTo( size.width * 0.3716610, size.height * 0.9580800, size.width * 0.3704600, size.height * 0.9442333, size.width * 0.3704600, size.height * 0.9300000); path_0.cubicTo( size.width * 0.3704600, size.height * 0.8305900, size.width * 0.4290000, size.height * 0.7500000, size.width * 0.5012107, size.height * 0.7500000); path_0.cubicTo( size.width * 0.5734213, size.height * 0.7500000, size.width * 0.6319613, size.height * 0.8305900, size.width * 0.6319613, size.height * 0.9300000); path_0.cubicTo( size.width * 0.6319613, size.height * 0.9442333, size.width * 0.6307603, size.height * 0.9580800, size.width * 0.6284939, size.height * 0.9713600); path_0.cubicTo( size.width * 0.6260944, size.height * 0.9854000, size.width * 0.6248959, size.height * 0.9924200, size.width * 0.6270775, size.height * 0.9962100); path_0.cubicTo(size.width * 0.6292591, size.height, size.width * 0.6340436, size.height, size.width * 0.6436150, size.height); path_0.lineTo(size.width * 0.9273608, size.height); path_0.cubicTo(size.width * 0.9616029, size.height, size.width * 0.9787240, size.height, size.width * 0.9893632, size.height * 0.9853567); path_0.cubicTo(size.width, size.height * 0.9707100, size.width, size.height * 0.9471400, size.width, size.height * 0.9000000); path_0.lineTo(size.width, size.height * 0.1000000); path_0.cubicTo( size.width, size.height * 0.05285967, size.width, size.height * 0.02928933, size.width * 0.9893632, size.height * 0.01464467); path_0.cubicTo(size.width * 0.9787240, 0, size.width * 0.9616029, 0, size.width * 0.9273608, 0); path_0.lineTo(size.width * 0.07263923, 0); path_0.cubicTo(size.width * 0.03839685, 0, size.width * 0.02127554, 0, size.width * 0.01063777, size.height * 0.01464467); path_0.close(); if (myBackground != null) { // 裁剪画布到自定义路径,仅显示路径内内容 canvas.clipPath(path_0); // 计算图片源区域与目标显示区域 final Rect sourceRect = Rect.fromLTWH(0, 0, myBackground!.width.toDouble(), myBackground!.height.toDouble()); final Rect targetRect = Rect.fromLTWH(0, 0, size.width, size.height); // 绘制适配后的图片 canvas.drawImageRect(myBackground!, sourceRect, targetRect, Paint()); } else { // 无图片时显示灰色背景 Paint paint0Fill = Paint()..style = PaintingStyle.fill; paint0Fill.color = Colors.grey.withOpacity(0.61); canvas.drawPath(path_0, paint0Fill); } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } }
方案2:ImageShader填充路径
通过ImageShader将图片作为路径的填充色,直接让图片适配路径形状与尺寸:
class BackgroundCustomPainter extends CustomPainter { final ui.Image? myBackground; const BackgroundCustomPainter(this.myBackground); @override void paint(Canvas canvas, Size size) { Path path_0 = Path(); // 保留原路径绘制代码,内容同上此处省略 path_0.close(); Paint fillPaint = Paint()..style = PaintingStyle.fill; if (myBackground != null) { // 创建图片Shader,铺满整个路径区域 final imageShader = ImageShader( myBackground!, TileMode.clamp, // 超出图片范围时不重复 TileMode.clamp, Matrix4.identity().storage, ); fillPaint.shader = imageShader; } else { fillPaint.color = Colors.grey.withOpacity(0.61); } // 用配置好的Paint绘制路径 canvas.drawPath(path_0, fillPaint); } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } }
效果说明
两种方案都能实现:
- 背景图完全适配CustomPaint的尺寸
- 背景图仅显示自定义路径范围内的部分,与路径形状完全一致
内容的提问来源于stack exchange,提问作者ILii
相关产品推荐
相关产品推荐

