Unverified Commit 79ad2d2e authored by Rene Floor's avatar Rene Floor Committed by GitHub

correctly dispose listeners by image widget (#57201)

parent 8abf0a6d
...@@ -1079,8 +1079,8 @@ class _ImageState extends State<Image> with WidgetsBindingObserver { ...@@ -1079,8 +1079,8 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (_isListeningToStream && if (_isListeningToStream &&
(widget.loadingBuilder == null) != (oldWidget.loadingBuilder == null)) { (widget.loadingBuilder == null) != (oldWidget.loadingBuilder == null)) {
_imageStream.removeListener(_getListener(oldWidget.loadingBuilder)); _imageStream.removeListener(_getListener());
_imageStream.addListener(_getListener()); _imageStream.addListener(_getListener(recreateListener: true));
} }
if (widget.image != oldWidget.image) if (widget.image != oldWidget.image)
_resolveImage(); _resolveImage();
...@@ -1119,22 +1119,25 @@ class _ImageState extends State<Image> with WidgetsBindingObserver { ...@@ -1119,22 +1119,25 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
_updateSourceStream(newStream); _updateSourceStream(newStream);
} }
ImageStreamListener _getListener([ImageLoadingBuilder loadingBuilder]) { ImageStreamListener _imageStreamListener;
loadingBuilder ??= widget.loadingBuilder; ImageStreamListener _getListener({bool recreateListener = false}) {
_lastException = null; if(_imageStreamListener == null || recreateListener) {
_lastStack = null; _lastException = null;
return ImageStreamListener( _lastStack = null;
_handleImageFrame, _imageStreamListener = ImageStreamListener(
onChunk: loadingBuilder == null ? null : _handleImageChunk, _handleImageFrame,
onError: widget.errorBuilder != null onChunk: widget.loadingBuilder == null ? null : _handleImageChunk,
? (dynamic error, StackTrace stackTrace) { onError: widget.errorBuilder != null
setState(() { ? (dynamic error, StackTrace stackTrace) {
_lastException = error; setState(() {
_lastStack = stackTrace; _lastException = error;
}); _lastStack = stackTrace;
} });
: null, }
); : null,
);
}
return _imageStreamListener;
} }
void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) { void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) {
......
...@@ -1331,6 +1331,73 @@ void main() { ...@@ -1331,6 +1331,73 @@ void main() {
expect(tester.binding.hasScheduledFrame, isFalse); expect(tester.binding.hasScheduledFrame, isFalse);
}, skip: isBrowser); }, skip: isBrowser);
testWidgets('Verify Image resets its ImageListeners', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
final TestImageStreamCompleter imageStreamCompleter = TestImageStreamCompleter();
final TestImageProvider imageProvider1 = TestImageProvider(streamCompleter: imageStreamCompleter);
await tester.pumpWidget(
Container(
key: key,
child: Image(
image: imageProvider1,
),
),
);
// listener from resolveStreamForKey is always added.
expect(imageStreamCompleter.listeners.length, 2);
final TestImageProvider imageProvider2 = TestImageProvider();
await tester.pumpWidget(
Container(
key: key,
child: Image(
image: imageProvider2,
excludeFromSemantics: true,
),
),
null,
EnginePhase.layout,
);
// only listener from resolveStreamForKey is left.
expect(imageStreamCompleter.listeners.length, 1);
});
testWidgets('Verify Image resets its ErrorListeners', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
final TestImageStreamCompleter imageStreamCompleter = TestImageStreamCompleter();
final TestImageProvider imageProvider1 = TestImageProvider(streamCompleter: imageStreamCompleter);
await tester.pumpWidget(
Container(
key: key,
child: Image(
image: imageProvider1,
errorBuilder: (_,__,___) => Container(),
),
),
);
// listener from resolveStreamForKey is always added.
expect(imageStreamCompleter.listeners.length, 2);
final TestImageProvider imageProvider2 = TestImageProvider();
await tester.pumpWidget(
Container(
key: key,
child: Image(
image: imageProvider2,
excludeFromSemantics: true,
),
),
null,
EnginePhase.layout,
);
// only listener from resolveStreamForKey is left.
expect(imageStreamCompleter.listeners.length, 1);
});
testWidgets('Image defers loading while fast scrolling', (WidgetTester tester) async { testWidgets('Image defers loading while fast scrolling', (WidgetTester tester) async {
const int gridCells = 1000; const int gridCells = 1000;
final List<TestImageProvider> imageProviders = <TestImageProvider>[]; final List<TestImageProvider> imageProviders = <TestImageProvider>[];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment