Issue
Preface:
I'm using the Screenshot package. In this package, there is a method captureAndSave()
which saves a widget as an image to a specific location, however, when I call this function, my image is not being saved. Why?
Complete Code Example:
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("Is the QR Code saved to your device? ${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);
// It always returns false, although I'm saving the file using `captureAndSave` .
doesTheImageExist = File(path).existsSync();
}
}
The Question:
In the code above, when I click on the TextButton()
that says "Save QR Code" it then calls _captureAndSaveQRCode()
and _loadImage()
. Hence my image should successfully be saved to my (Android) phone. However, I get an error:
Unable to load asset: /data/user/0/com.example.qr/app_flutter/qr.png
Full Traceback:
======== 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: AssetBundleImageKey(bundle: PlatformAssetBundle#5986d(), name: "/data/user/0/com.example.qr/app_flutter/qr.png", scale: 1.0)
====================================================================================================
- Why isn't my image being saved to the device when calling
_captureAndSaveQRCode()
?
Side note:
I recently posted an answer (currently in Bounty) with (almost) the same code as in this question which does work correctly, so, what's the difference?
Solution
The problem was that I had an empty setState
:
onPressed: () async {
await _captureAndSaveQRCode();
image = await _loadImage();
setState(() {});
},
)
So to solve the problem, I removed the setState
and also got rid of the _loadImage()
function.
And then updated the image
variable within the TextButton()
:
TextButton(
child: Text("Save QR Code"),
onPressed: () async {
await _captureAndSaveQRCode();
setState(() {
doesTheImageExist = true;
image = image;
});
},
)
Complete working example:
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();
setState(() {
doesTheImageExist = true;
image = image;
});
},
)
else
image!,
Row(children: [
Text('hi'),
Text("Is the QR Code saved to your device? ${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);
// It always returns false, although I'm saving the file using `captureAndSave` .
doesTheImageExist = File(path).existsSync();
}
}
Answered By - MendelG
Answer Checked By - Gilberto Lyons (JavaFixing Admin)