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,13 +1119,14 @@ class _ImageState extends State<Image> with WidgetsBindingObserver { ...@@ -1119,13 +1119,14 @@ 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}) {
if(_imageStreamListener == null || recreateListener) {
_lastException = null; _lastException = null;
_lastStack = null; _lastStack = null;
return ImageStreamListener( _imageStreamListener = ImageStreamListener(
_handleImageFrame, _handleImageFrame,
onChunk: loadingBuilder == null ? null : _handleImageChunk, onChunk: widget.loadingBuilder == null ? null : _handleImageChunk,
onError: widget.errorBuilder != null onError: widget.errorBuilder != null
? (dynamic error, StackTrace stackTrace) { ? (dynamic error, StackTrace stackTrace) {
setState(() { setState(() {
...@@ -1136,6 +1137,8 @@ class _ImageState extends State<Image> with WidgetsBindingObserver { ...@@ -1136,6 +1137,8 @@ class _ImageState extends State<Image> with WidgetsBindingObserver {
: null, : null,
); );
} }
return _imageStreamListener;
}
void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) { void _handleImageFrame(ImageInfo imageInfo, bool synchronousCall) {
setState(() { setState(() {
......
...@@ -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