Flutter 如何实现后台生成下载PDF 带进度通知点击打开功能
问题核心优化方案
一、先解决PDF生成慢的核心瓶颈
你现有代码最大的耗时点在循环内串行拉取商品图片,300个商品串行发起300次网络请求,耗时会指数级上升,优先优化这部分:
- 提前在购物车页面加载商品列表时,就把所有商品图片缓存到本地目录,生成PDF时直接读取本地缓存文件,完全避免生成阶段的网络请求
- 若无法提前缓存,改用并发请求批量拉取所有图片,不要在for循环里逐个await网络请求,300个并发请求的耗时远低于300次串行请求
- 所有插入PDF的图片提前压缩到显示所需的尺寸(比如宽度适配PDF列宽、分辨率控制在72DPI),不要直接插入原图,既减小PDF体积也加快生成速度
- 删除循环内所有print语句,debug模式下高频打印会拖慢执行速度;删掉多余的sr数组,直接用循环变量计算序号即可,减少不必要的内存操作
二、PDF生成逻辑移至后台Isolate
syncfusion的PDF生成属于CPU密集型任务,跑在主Isolate会阻塞UI,用Flutter的后台Isolate执行生成逻辑:
- 提前把所有需要用到的商品数据(本地缓存的图片字节、SKU、重量、数量等)整理成可跨Isolate传输的纯数据对象
- 用
Isolate.run()或者compute()函数把整个PDF生成逻辑放到后台Isolate执行,主Isolate只负责接收进度和最终生成的PDF文件路径
三、进度通知实现
用flutter_local_notifications实现类Google Drive的进度通知:
- 点击下载按钮时先请求通知权限,初始化通知通道,弹出初始进度为0的通知
- 后台生成PDF的过程中,每处理N个商品(比如每处理5个、或者每完成1%进度)就向主Isolate发送当前进度,主Isolate收到后更新通知的进度条和文案,用同一个通知ID更新就会覆盖之前的通知,不会重复弹出
- 生成完成后,更新通知内容为「PDF生成完成,点击打开」,给通知设置点击回调,调用
OpenFile.open()打开生成好的PDF文件即可
四、其他细节优化
- 你当前只添加了一个PDF页面,300个商品肯定会超出单页高度,syncfusion的PdfGrid支持自动分页,调用
grid.draw()时不要指定固定page,让插件自动生成多页,避免内容截断 - PDF保存路径可以改用公共下载目录,方便用户在文件管理器里直接找到导出的文件
核心优化代码示例
// 1. 提前批量缓存图片示例 Future<List<Uint8List>> preloadAllImages(List<String> photoUrls) async { final futures = photoUrls.map((url) async { final response = await get(Uri.parse(url)); // 可以在这里加图片压缩逻辑 return response.bodyBytes; }); // 并发请求所有图片 return Future.wait(futures); } // 2. 后台Isolate生成PDF的入口 static Future<String> generatePdfInBackground(Map<String, dynamic> params) async { // 这里放你原来的PDF生成逻辑,所有需要的数据都从params取 // 生成过程中可以发送进度事件,用ReceivePort/SendPort通信 final List<Uint8List> images = params['images']; final List<String> skuArray = params['skuArray']; // ... 其余生成逻辑和你原来的一致 // 最终返回生成的PDF文件路径 return filePath; } // 3. 更新通知进度示例 void updateNotificationProgress(int current, int total) { final progress = (current / total * 100).round(); flutterLocalNotificationsPlugin.show( 1001, // 固定通知ID,用来覆盖更新 '正在生成PDF', '已完成$progress%', NotificationDetails( android: AndroidNotificationDetails( 'pdf_download_channel', 'PDF下载', channelDescription: 'PDF生成进度通知', importance: Importance.low, priority: Priority.low, showProgress: true, maxProgress: 100, progress: progress, ongoing: true, ), ), ); }
内容的提问来源于stack exchange,提问作者Samkit Shah
相关产品推荐
相关产品推荐

