Skip to content

Conversation

@dnfield
Copy link
Contributor

@dnfield dnfield commented Mar 12, 2020

Description

If resolving the image for an Image widget results in an exception, we don't provide a good way to catch and try to handle that exception today. This adds an errorBuilder callback to the Image widget so that callers can decide to provide a replacement widget and/or do logging or analytics on failures.

/cc @escamoteur @slightfoot

Related Issues

Fixes #44579 (again :D)

Tests

I added the following tests:

  • Test that not providing an errorHandle throws up to FlutterError (existing behavior)
  • Test that throwing during the common places an ImageProvider might throw result in calling the error handler with the expected parameters, and that the returned widget is built.

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I signed the CLA.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I updated/added relevant documentation (doc comments with ///).
  • All existing and new tests are passing.
  • The analyzer (flutter analyze --flutter-repo) does not report any problems on my PR.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Did any tests fail when you ran them? Please read Handling breaking changes.

  • No, no existing tests failed, so this is not a breaking change.

@dnfield dnfield added the a: images Loading, displaying, rendering images label Mar 12, 2020
@fluttergithubbot fluttergithubbot added the framework flutter/packages/flutter repository. See also f: labels. label Mar 12, 2020
@dnfield dnfield requested a review from gaaclarke March 12, 2020 17:38
);

/// Signature for the method that is called when loading an image results in an
/// error.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include a link to [Image.errorBuilder] in this doc comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Reworded it a bit as well to indicate what it does.


/// A builder function that is called if an error occurs during image loading.
///
/// If this builder is not provided, any exceptions will be reporetd to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spelling: reporetd

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whups. done.

/// A builder function that is called if an error occurs during image loading.
///
/// If this builder is not provided, any exceptions will be reporetd to
/// [FlutterError.onError]. If it is provided, the caller should either handle
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should mention that handling means returning a replacement widget that will render instead of the image.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

/// child: Image.network(
/// 'https://example.does.not.exist/image.jpg',
/// errorBuilder: (BuildContext context, Object exception, StackTrace stackTrace) {
/// print('An error occurred loading "https://example.does.not.exist/image.jpg": $exception');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just remove the print. In general it wouldn't be good practice to have one here in an app and examples should show good practice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced the print with a comment indicating how you might use this for analytics purposes.

/// error.
typedef ImageErrorWidgetBuilder = Widget Function(
BuildContext context,
Object error,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why Object instead of dynamic here? ImageErrorListener types this as dynamic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think ImageErrorListener should probably be Object, and I'm trying to avoid introducing dynamic to another place in the framework. In particular for this, this gets surfaced at a higher level in the API, and it would be good to avoid people interacting with this object without casting it to something proper. In particular, if they call some method on the dynamic object without casting it, it will prevent tree shaking for all types that have that method :\

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

Copy link
Member

@goderbauer goderbauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

/// error.
typedef ImageErrorWidgetBuilder = Widget Function(
BuildContext context,
Object error,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

@escamoteur
Copy link
Contributor

awesome!!!!

@lock
Copy link

lock bot commented Apr 2, 2020

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

@lock lock bot locked and limited conversation to collaborators Apr 2, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

a: images Loading, displaying, rendering images framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Failure of ImageProvider's error handling

5 participants