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

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()方法时,为什么图片没有保存到设备中?

补充说明

我此前发布的回答(当前处于悬赏阶段)使用了和本题几乎完全一致的代码却可以正常运行,请问二者的差异是什么?


问题原因与修复方案

代码存在两处核心错误,和插件本身功能无关:

  1. captureAndSave参数传递错误
    该方法的第一个入参要求传入保存目录的路径,不是带文件名的完整存储路径。把拼接了qr.png的完整文件路径当成目录传入后,插件会将该路径识别为目标文件夹,尝试在其下生成随机名称的截图文件,自然不会在指定路径生成qr.png文件,所以文件存在性检查永远返回false。

  2. 本地文件加载方式错误
    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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 08:33:12