mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
correctly dispose listeners by image widget (#57201)
This commit is contained in:
parent
8abf0a6d8c
commit
79ad2d2e20
@ -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 {
|
|||||||
_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() {
|
|||||||
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>[];
|
||||||
|
Loading…
Reference in New Issue
Block a user