Flutter Screenshot插件captureAndSave() Android端存图失败问题
我正在使用Screenshot插件。该插件提供captureAndSave()方法,可将组件以图片形式保存到指定位置,但调用该函数时图片并未成功保存,是什么原因?
import 'dart:io'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:screenshot/screenshot.dart'; class QrCodeScreen extends StatefulWidget { const QrCodeScreen({Key? key}) : super(key: key); @override State<QrCodeScreen> createState() => _QrCodeScreenState(); } class _QrCodeScreenState extends State<QrCodeScreen> { final _screenshotController = ScreenshotController(); Image? image; var doesTheImageExist = false; @override Widget build(BuildContext context) { return Scaffold( body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ if (image == null) TextButton( child: Text("Save QR Code"), onPressed: () async { await _captureAndSaveQRCode(); image = await _loadImage(); setState(() {}); }, ) else image!, Text("二维码是否已保存到设备? ${doesTheImageExist}"), if (image == null) Screenshot( controller: _screenshotController, child: _buildQRImage('_authProvider.user!.uid')), ], ), ); } Widget _buildQRImage(String data) { return QrImage( data: data, size: 250.0, gapless: false, foregroundColor: Colors.black, backgroundColor: Colors.white, ); } Future<String> get imagePath async { final directory = (await getApplicationDocumentsDirectory()).path; return '$directory/qr.png'; } Future<Image> _loadImage() async { return imagePath.then((imagePath) => Image.asset(imagePath)); } Future<void> _captureAndSaveQRCode() async { final path = await imagePath; await _screenshotController.captureAndSave(path); // 尽管我调用`captureAndSave`保存文件,此处始终返回false doesTheImageExist = File(path).existsSync(); } }
上述代码中,点击显示为*"Save QR Code"*的TextButton()组件后,会依次调用_captureAndSaveQRCode()和_loadImage()方法,按照预期图片应当成功保存到Android手机中,但实际运行抛出如下错误:
Unable to load asset: /data/user/0/com.example.qr/app_flutter/qr.png

完整报错堆栈:
======== Exception caught by image resource service ================================================ The following assertion was thrown resolving an image codec: Unable to load asset: /data/user/0/com.example.qr/app_flutter/qr.png When the exception was thrown, this was the stack: #0 PlatformAssetBundle.load (package:flutter/src/services/asset_bundle.dart:237:7) <asynchronous suspension> #1 AssetBundleImageProvider._loadAsync (package:flutter/src/painting/image_provider.dart:675:14) <asynchronous suspension> Image provider: AssetImage(bundle: null, name: "/data/user/0/com.example.qr/app_flutter/qr.png") Image key: AssetImage(bundle: PlatformAssetBundle#5986d(), name: "/data/user/0/com.example.qr/app_flutter/qr.png", scale: 1.0) ====================================================================================================
- 调用
_captureAndSaveQRCode()方法时,为什么图片没有保存到设备中?
补充说明
我此前发布的回答(当前处于悬赏阶段)使用了和本题几乎完全一致的代码却可以正常运行,请问二者的差异是什么?
代码存在两处核心错误,和插件本身功能无关:
captureAndSave参数传递错误
该方法的第一个入参要求传入保存目录的路径,不是带文件名的完整存储路径。把拼接了qr.png的完整文件路径当成目录传入后,插件会将该路径识别为目标文件夹,尝试在其下生成随机名称的截图文件,自然不会在指定路径生成qr.png文件,所以文件存在性检查永远返回false。本地文件加载方式错误
Image.asset仅用于加载App安装包内打包的静态资源,存储在应用私有目录的截图属于设备本地文件,用Image.asset加载必然会抛出找不到资源的错误。
修复代码
首先修改截图保存逻辑,传入目录路径,通过fileName参数指定文件名:
Future<void> _captureAndSaveQRCode() async { final directory = await getApplicationDocumentsDirectory(); // 第一个参数传目录路径,单独指定文件名 final finalPath = await _screenshotController.captureAndSave( directory.path, fileName: 'qr.png', ); doesTheImageExist = File(finalPath!).existsSync(); }
然后修改图片加载逻辑,使用Image.file加载本地文件:
Future<Image> _loadImage() async { final path = await imagePath; return Image.file(File(path)); }
由于使用的是应用私有文档目录存储截图,不需要额外申请存储权限,改完上述两处即可正常运行。
至于提到的之前同逻辑代码可以正常运行,本质是代码细节存在差异:要么之前的代码传入的是纯目录路径、没有指定固定文件名,要么加载截图时用的是capture方法返回的内存数据通过Image.memory加载,或是正确使用了Image.file,和当前版本的写法并不完全一致。
内容的提问来源于stack exchange,提问作者MendelG

