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

Flutter生成PDF报错:'!_paint' 文档已保存断言异常

Flutter PDF二次生成报错:The document has already been saved 修复方案

问题现象

  • 首次触发PDF生成、打开功能可正常运行
  • 新增/编辑业务数据后第二次触发生成操作时,输出的PDF文件仍为第一次生成的旧内容,同时抛出未处理异常:
'package:pdf/src/widgets/document.dart': Failed assertion: line 117 pos 12: '!_paint': The document has already been saved
  • 现有代码逻辑:
    • 按钮点击回调中组装最新的Invoice业务对象,调用PdfInvoiceApiDsr.generate(invoice)生成PDF,再调用PdfApi.openFile(pdfFile)打开文件
    • PdfInvoiceApiDsr.dart中在类外部全局定义了final pdf = pw.Document()实例,同时全局定义了依赖该pdf实例的imageA对象;generate静态方法内直接调用该全局pdf实例的addPage方法添加页面时触发报错
    • PdfApi类的saveDocument方法接收文件名、Document类型pdf参数,调用pdf.save()获取字节流后写入应用文档目录,openFile方法调用OpenFile.open打开对应路径文件

问题原因

pdf包的Document对象调用save()方法后,内部_paint标记会被置为true,锁定文档状态,不允许再添加页面、修改内容。全局定义的pdf实例是全局单例,第一次生成时调用save()后实例已经被锁定,第二次生成时复用这个被锁定的实例,既无法添加新页面,之前生成的旧内容也残留在实例中,就会触发断言报错,同时生成的文件还是旧内容。全局绑定旧pdf实例的imageA对象也会复用旧实例的状态,加剧内容异常问题。

修复步骤

  • 删除PdfInvoiceApiDsr.dart类外部全局定义的pw.Document()实例,以及绑定该全局实例的全局imageA对象
  • 在generate静态方法内部,每次调用时都新建一个全新的pw.Document()实例,所有依赖该实例的资源(比如imageA)都在方法内部基于新实例初始化,不做全局复用
  • 生成PDF文件名时增加动态标识(比如当前时间戳),同时写入文件前先检查目标路径,若存在旧文件先删除再写入新字节流,避免系统缓存或旧文件残留导致打开时显示旧内容
  • 参考修正后的代码逻辑:
// PdfInvoiceApiDsr.dart 修正后generate方法
static Future<File> generate(Invoice invoice) async {
  // 每次生成都新建全新Document实例
  final pdf = pw.Document();
  // 所有依赖pdf实例的资源在方法内初始化,和当前实例绑定
  final imageA = await _loadInvoiceImage(); // 替换为你原有imageA的初始化逻辑

  pdf.addPage(
    pw.MultiPage(
      // 原有页面构建逻辑,传入invoice、imageA等参数即可
      build: (context) => [
        // 发票标题、供应商信息、账单条目、签名等组件
      ]
    )
  );

  // 文件名加时间戳避免缓存
  final outputFileName = 'invoice_${DateTime.now().millisecondsSinceEpoch}.pdf';
  return PdfApi.saveDocument(fileName: outputFileName, pdf: pdf);
}
// PdfApi.dart saveDocument方法补充旧文件删除逻辑
static Future<File> saveDocument({required String fileName, required pw.Document pdf}) async {
  final bytes = await pdf.save();
  final dir = await getApplicationDocumentsDirectory();
  final file = File('${dir.path}/$fileName');
  // 存在旧文件先删除
  if (await file.exists()) {
    await file.delete();
  }
  await file.writeAsBytes(bytes);
  return file;
}

内容的提问来源于stack exchange,提问作者LKC

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:12:28