I'm working with the Camera plugin in Flutter and by default when it captures the image it returns it in XFile format. I've noticed that the quality of the image is really good inside the camera preview screen before capturing, it has the same quality as the native camera app on my phone. But when I capture the image and try to send the image file to another screen and display it in an Image.file() widget, the quality drops and it becomes very grainy. I was trying to find out if that's an issue related with the XFile format or the camera plugin itself.
I have tried adding a delay before capture and I have tried to use two different devices, but it doesn't solve the grainy photo problem. Below you'll find some screenshots to better demonstrate the problem, and along with it is the code I'm using to capture, save and display the image across my screens.
P.S. I have tried to research more on XFile but there isn't much on the web except the documentation and it doesn't offer a lot of helpful information, so any help is much appreciated.
Screenshots:
In Camera Preview (Live camera, quality is good):
In PreviewScreen (post-capture, you can see quality is getting a bit grainy):
In PhotoTakenScreen (very grainy, quality is very bad):
Code:
Inside CameraScreen (on pressing the capture button):
onPressed: () async{
var path;
if (cameraController!.value.isInitialized) {
await cameraController!.takePicture().then((XFile file) {
if (mounted) {
path = file.path;
print(path);
}
Future.delayed(const Duration(milliseconds: 500), () {
Navigator.pushNamed(
context,
cameraPreviewRoute,
arguments: PreviewScreen(
imageFile: File(file.path),
),
);
});
}
In PreviewScreen:
....
Scaffold(
body: body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
flex: 2,
child: Image.file(
File(widget.imageFile.path),
fit: BoxFit.cover,
),
),
],
),
),
)
There is a button in PreviewScreen widget that goes to PhotoTakenScreen, this is what it does:
onPressed: () async {
try {
//adding a toast here because saving _loadImage takes a few seconds before moving to the next screen
showToast("Please wait while image is being processed..", false,
info: true);
File savedImage;
await _loadImage(widget.imageFile.path).then((value) {
savedImage = value!;
// for debugging
print(savedImage.path);
//send the image object over to phototaken
Navigator.pushNamed(
context,
photoTakenScreen,
arguments: PhotoTakenScreen(
imagePath: savedImage.path,
),
);
}
loadImage() is a function that calls saveImageToDisk() which saves the image to the disk in a separate thread and returns the saved file. Here they are:
Future<File?> _loadImage(String imagePath) async {
try {
File mFile;
Directory directory = await getTemporaryDirectory();
Map map = Map();
map['path'] = imagePath;
map['directory'] = directory;
// run _saveImageToDisk in a separate thread
mFile = await compute(saveImageToDisk, map);
return mFile;
} catch (err) {
rethrow;
}
}
Future<File> saveImageToDisk(Map pathAndDirectory) async {
try {
String path = pathAndDirectory['path'];
Directory directory = pathAndDirectory['directory'];
File tempFile = File(path);
Img.Image? image = Img.decodeImage(tempFile.readAsBytesSync());
if (image == null) throw ('Error: Could not decode image');
Img.Image mImage = Img.copyResizeCropSquare(image, 224);
String imgType = path.split('.').last;
String mPath =
'${directory.path.toString()}/image_${DateTime.now()}.$imgType';
mPath = mPath.replaceAll(
new RegExp(r"\s+\b|\b\s"), ""); //remove all spaces from img path
File dFile = File(mPath);
if (imgType == 'jpg' || imgType == 'jpeg') {
dFile.writeAsBytesSync(Img.encodeJpg(mImage));
} else {
dFile.writeAsBytesSync(Img.encodePng(mImage));
}
return dFile;
} catch (e) {
rethrow; //rethrow to alert _loadImage in PreviewScreen()
}
}


