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 {
super.didUpdateWidget(oldWidget);
if (_isListeningToStream &&
(widget.loadingBuilder == null) != (oldWidget.loadingBuilder == null)) {
_imageStream.removeListener(_getListener(oldWidget.loadingBuilder));
_imageStream.addListener(_getListener());
_imageStream.removeListener(_getListener());
_imageStream.addListener(_getListener(recreateListener: true));
}
if (widget.image != oldWidget.image)
_resolveImage();
......@@ -1119,22 +1119,25 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
_updateSourceStream(newStream);
}
ImageStreamListener _getListener([ImageLoadingBuilder loadingBuilder]) {
loadingBuilder ??= widget.loadingBuilder;
_lastException = null;
_lastStack = null;
return ImageStreamListener(
_handleImageFrame,
onChunk: loadingBuilder == null ? null : _handleImageChunk,
onError: widget.errorBuilder != null
? (dynamic error, StackTrace stackTrace) {
setState(() {
_lastException = error;
_lastStack = stackTrace;
});
}
: null,
);
ImageStreamListener _imageStreamListener;
ImageStreamListener _getListener({bool recreateListener = false}) {
if(_imageStreamListener == null || recreateListener) {
_lastException = null;
_lastStack = null;
_imageStreamListener = ImageStreamListener(
_handleImageFrame,
onChunk: widget.loadingBuilder == null ? null : _handleImageChunk,
onError: widget.errorBuilder != null
? (dynamic error, StackTrace stackTrace) {
setState(() {
_lastException = error;
_lastStack = stackTrace;
});
}
: null,
);
}
return _imageStreamListener;
}
void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) {
......
......@@ -1331,6 +1331,73 @@ void main() {
expect(tester.binding.hasScheduledFrame, isFalse);
}, 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 {
const int gridCells = 1000;
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