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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 00:48:11