2

My flutter app saves screenshots to disk (on Flutter desktop Mac and Windows). If writing fails due to whatever reason catch and error handling does not work. Statements after "catch" will never be executed despite debug output shows

[ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: FileSystemException: Cannot open file ...]

What am I missing here?

actual code snippet:

final dFile = File('$dPath(AS-Logo) $logoText.png');
screenshotController
    .captureFromWidget(makeLogoShow(completeImageSize, logoText))
    .then((capturedImage) {
  try {
    dFile.writeAsBytes(capturedImage);
  } on FileSystemException catch (e) {
    print("Error: $e");
    return false;
  }
});
gundrabur
  • 843
  • 10
  • 12
  • Your `try`-`catch` doesn't work because `writeAsBytes` is asynchronous but you don't `await` its completion within the `try` block. In general, strongly prefer using `async`-`await` instead of `Future.then`. It's much easier to use and avoids problems likes this. Also enable the `unawaited_futures` lint. – jamesdlin Aug 16 '22 at 18:09
  • Ok, I made the function async and put "await" before the ScreenshotController" statement. Nothing changed ... – gundrabur Aug 16 '22 at 18:27
  • Did you add `await` for the `writeAsBytes` call? That's the important part. – jamesdlin Aug 16 '22 at 18:54
  • flutter won't allow me to put the await statement directly in front of "dFile.writeAsBytes ...". I am getting an error "please mark your function as "async" which I did! I think that's because we are in a ".then" closure there ... The error message disappears only if I am putting "await" in Front of "screenshotController". – gundrabur Aug 16 '22 at 19:10
  • 2
    The closure must be marked `async` to use `await` inside of it: `.then((capturedImage) async { ... }`. But really you should not bother with the `.then` closure and use `screenshotController.captureFromWidget(...)` *also*. – jamesdlin Aug 16 '22 at 20:00
  • I did that, but it didn't work either. No changes in behavior. I made a gist containing the actual code of the function now. Please look here: https://gist.github.com/gundrabur/2a3f7902912871ba47b918217b0646bb – gundrabur Aug 17 '22 at 16:53

1 Answers1

1

So the way I solved this was to write a different function to convert the widget to an Image file.

File _processWidgetImage() {
  String yourFilePath = 'your/file/path.png';
  Future<Uint8List> image = screenshotController
  .captureFromWidget(makeLogoShow(completeImageSize, logoText));
  Future<File> f = _widgetToImageFile(image, yourFilePath);
  return await f;
}

Future<File> _widgetToImageFile(Future<Uint8List> capturedImage, String path) async {
  Uint8List cp = await capturedImage;
  final dFile = await File(path).writeAsBytes(cp);
  return dFile;
}

You should post your makeLogoShow method. There may be something in there that's not working right also. That method needs to return a viewable widget.

returnVoid
  • 604
  • 1
  • 9
  • 17
  • the "makeLogoShow" method is perfectly working, both on showing the widget on the screen and actually saving it to disk. Even the save method is working fine but I cannot get error handling if something fails during writing (e.g. no write permission on the destination folder) – gundrabur Aug 16 '22 at 18:57
  • @gundrabur did you try just using Exception? Another thing to consider, which may be OS dependent, is the String you use for your file path. Not all OS's will allow spaces in the file path. – returnVoid Aug 17 '22 at 15:53
  • Maybe I did not express myself correctly: All is working fine right now. No errors! I only will catch filesystem errors IF some may occur. eg. the user selects a folder to which he does not have write permissions ... – gundrabur Aug 17 '22 at 16:34