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

Flutter中CustomPaint与PictureRecorder绘制尺寸不一导致水印变小如何解决

问题原因

你遇到的尺寸差异核心是单位不统一:

  • 你在CustomPaint预览时用的fontSize是Flutter默认的逻辑像素单位,适配的是屏幕显示尺寸(比如预览区域宽度只有300逻辑像素,20号字体看起来大小合适)
  • 导出时PictureRecorder的画布尺寸是原图的物理像素尺寸(比如原图是4000px宽),同样的20单位字体放到4000px的画布上,占比只有预览时的1/13左右,自然看起来变小。

修复方案

有两种常用的实现方案,你可以根据需求选:

方案1:按原图比例动态计算水印尺寸(推荐,不需要依赖预览控件尺寸)

直接把所有水印相关的尺寸(字体大小、logo大小、位置偏移)改成按原图尺寸的比例计算,保证不管原图多大,水印的视觉占比一致。
比如设置字体大小为原图宽度的1/50,logo宽度为原图宽度的1/10:

// 导出代码修改示例
ui.PictureRecorder recorder = ui.PictureRecorder();
Canvas canvas = Canvas(recorder);
ui.Image image = await _retrieveImage();

// 动态计算缩放后的字体大小
double scaledFontSize = image.width / 50; // 可根据需求调整比例分母
// 如果logo需要缩放也同理计算,logoAspectRatio为你原始logo的宽高比
Size scaledLogoSize = Size(image.width / 10, image.width /10 * logoAspectRatio);

Watermark.draw(
    canvas,
    image,
    text,
    TextStyle(
        fontSize: scaledFontSize, // 替换成动态计算的尺寸
        color: Color.fromRGBO(255, 255, 255, fontOpacity)),
    Size(image.width.roundToDouble(), image.height.roundToDouble()),
    _logo,
    scaledLogoSize: scaledLogoSize // 把缩放后的logo尺寸传入绘制方法
);

ui.Image pic = await recorder.endRecording().toImage(image.width, image.height);
ByteData data = await pic.toByteData(format: ui.ImageByteFormat.png);

对应的CustomPaint预览代码也同步修改,用预览的图片显示宽度作为基准计算尺寸,保证预览和导出效果一致:

// 用LayoutBuilder获取CustomPaint的实际显示宽度
LayoutBuilder(
  builder: (context, constraints) {
    double previewWidth = constraints.maxWidth;
    double previewFontSize = previewWidth /50; // 和导出的比例保持一致
    return CustomPaint(
      painter: painter.ImagePainter(
        image: snapshot.data,
        text: text,
        style: TextStyle(
          fontSize: previewFontSize,
          color: Color.fromRGBO(255, 255, 255, fontOpacity),
        ),
        logo: _logo,
        // 同样按预览宽度计算logo显示尺寸
        previewLogoSize: Size(previewWidth/10, previewWidth/10 * logoAspectRatio),
      ),
      willChange: true,
    );
  }
)

方案2:用固定缩放系数换算

如果你需要保持字体大小的设置值和预览效果完全一致,可以先获取预览时CustomPaint的实际渲染宽度,计算导出时的缩放系数:

// 缩放系数 = 原图宽度 / 预览区域宽度
double exportScale = image.width / previewCustomPaintWidth;
// 所有尺寸乘以缩放系数
double scaledFontSize = fontSize * exportScale;

额外注意点

  • 水印的位置偏移也要同步乘以对应的缩放系数,不然会出现水印位置和预览不一致的问题
  • 如果需要支持横竖图不同的缩放逻辑,可以取原图宽高中较小的值作为比例计算的基准,避免竖图水印过大或者横图水印过小

内容的提问来源于stack exchange,提问作者Burak Mete Erdoğan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 21:27:05