Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix: Ensure Image.errorBuilder reliably prevents error reporting (with addEphemeralErrorListener) #167783

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

dkwingsmt
Copy link
Contributor

@dkwingsmt dkwingsmt commented Apr 24, 2025

An alternative to #166130 using addEphemeralErrorListener.
The following text is adapted from #166130.

Problem:

Currently, when using an Image widget with an errorBuilder, if the widget is removed from the widget tree (e.g., due to navigation or setState) after the image loading process has started but before an asynchronous loading error is reported back, the error can still be reported via FlutterError.reportError. This occurs because the _ImageState listener is removed upon disposal, and the ImageStreamCompleter subsequently treats the error as unhandled, logging it despite the developer's intent to handle it via the errorBuilder. This leads to unexpected noise in logs and crash reporting systems.

Solution:

This PR utilizes addEphemeralErrorListener, which allows the image stream to be disposed while having an error reporter. The error will not be reported as long as there is an error reporter. The Image widget adds an empty error reporter if errorBuilder is not null.

Related Issues:

Tests:

  • Added a new test case errorBuilder prevents FlutterError report even if widget is disposed to test/widgets/image_test.dart to specifically verify the fix for the disposal race condition.
  • Existing tests in test/widgets/image_test.dart (including golden tests like 'Failed image loads in debug mode') pass with these changes without requiring updates.

Breaking Changes:

  • None. This change fixes incorrect behavior and preserves expected debug visuals. The internal mechanism for reporting errors when no errorBuilder is present has shifted, but the user-facing outcome is consistent.

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos a: desktop Running on desktop labels Apr 24, 2025
@dkwingsmt dkwingsmt force-pushed the fix-image-error-with-ephemeral branch from 3710426 to 5bc81d1 Compare April 24, 2025 23:26
@github-actions github-actions bot removed d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos a: desktop Running on desktop labels Apr 24, 2025
@dkwingsmt dkwingsmt marked this pull request as ready for review April 28, 2025 21:41
@chunhtai chunhtai self-requested a review April 28, 2025 21:53
@@ -1180,6 +1180,21 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
: null,
),
);
if (widget.errorBuilder != null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this support the following sequence?

  1. widget build with widget.errorBuilder != null
  2. widget rebuild with widget.errorBuilder == null
  3. widget dispose
  4. image failed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a great point! I've added a test for it and adjusted code to satisfy this case.

// allowing the disposal. For more details, see
// https://github.com/flutter/flutter/issues/97077 .
if (newStream.completer == null) {
newStream.setCompleter(_EmptyImageCompleter());
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain a bit why this is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The ephemeralListener is only applicable to image completers. However, a stream might not be resolved with a non-null completer (for various reasons; it's nullable anyway). This tricks is also used somewhere else, such as

stream.setCompleter(_ErrorImageCompleter());
.

// https://github.com/flutter/flutter/issues/97077 .
if (widget.errorBuilder != null) {
if (_imageStream!.completer == null) {
_imageStream!.setCompleter(_EmptyImageCompleter());
Copy link
Contributor

Choose a reason for hiding this comment

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

If there is no completer, adding this won't do anything right? since it will never complete, should we just return in this case?

I looked at the code, and it looked like that even we set this completer and addEphemeralErrorListener, the EphemeralErrorListener will not carry over if the completer change later, so it won't be able to mute the error after that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean that a stream without a completer will not cause any error, and it isn't useful to add ephemeral listeners any more?

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you mean that a stream without a completer will not cause any error, and it isn't useful to add ephemeral listeners any more?

technically yes.

but i am more focusing on that adding an empty completer to add a ephemeral listener seems to be the same as not adding an ephemeral listener at all. The empty completer is never going to throw, and changing the completer won't carry the ephemeral listener over.

As for whether it should mute the error throw after changing the completer if widget.errorbuilder != null, I am not sure what the correct behavior should be.

Copy link
Contributor Author

@dkwingsmt dkwingsmt Apr 29, 2025

Choose a reason for hiding this comment

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

but i am more focusing on that adding an empty completer to add a ephemeral listener seems to be the same as not adding an ephemeral listener at all. The empty completer is never going to throw, and changing the completer won't carry the ephemeral listener over.

That makes sense. I'll look more into it and remove the empty listener if that's the case.

As for whether it should mute the error throw after changing the completer if widget.errorbuilder != null, I am not sure what the correct behavior should be.

For now it will mute the error again. It simply takes whatever case at the moment of widget disposal. I think this is an intuitive behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The code seems to show that errors are only triggered from within the completer. Therefore when there's no completers there is no need to create one. I've removed the creation.

@dkwingsmt dkwingsmt requested a review from chunhtai April 30, 2025 23:24
@dkwingsmt
Copy link
Contributor Author

@chunhtai Can you take another look?

Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

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

LGTM

@dkwingsmt dkwingsmt added the autosubmit Merge PR when tree becomes green via auto submit App label May 1, 2025
@auto-submit auto-submit bot added this pull request to the merge queue May 1, 2025
Merged via the queue into flutter:master with commit e5e8c48 May 1, 2025
76 checks passed
@flutter-dashboard flutter-dashboard bot removed the autosubmit Merge PR when tree becomes green via auto submit App label May 1, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 1, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 2, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 2, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 2, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 2, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 3, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 3, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 4, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 6, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 6, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 6, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
framework flutter/packages/flutter repository. See also f: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Navigating to a new page causes the Image errorBuilder to be ignored.
2 participants