Image provider fix (#49920)
* Fix image provider missing early return * test * comments * whitespace, another comment
This commit is contained in:
parent
a1aa3c5b3d
commit
8600d2305e
@ -81,6 +81,7 @@ class ScrollAwareImageProvider<T> extends ImageProvider<T> {
|
||||
// Something else got this image into the cache. Return it.
|
||||
if (PaintingBinding.instance.imageCache.containsKey(key)) {
|
||||
imageProvider.resolveStreamForKey(configuration, stream, key, handleError);
|
||||
return;
|
||||
}
|
||||
// The context has gone out of the tree - ignore it.
|
||||
if (context.context == null) {
|
||||
|
@ -13,8 +13,8 @@ void main() {
|
||||
imageCache.clear();
|
||||
});
|
||||
|
||||
RecordingPhysics _findPhysics(WidgetTester tester) {
|
||||
return Scrollable.of(find.byType(TestWidget).evaluate().first).position.physics as RecordingPhysics;
|
||||
T _findPhysics<T extends ScrollPhysics>(WidgetTester tester) {
|
||||
return Scrollable.of(find.byType(TestWidget).evaluate().first).position.physics as T;
|
||||
}
|
||||
|
||||
ScrollMetrics _findMetrics(WidgetTester tester) {
|
||||
@ -83,7 +83,7 @@ void main() {
|
||||
testImageProvider.complete();
|
||||
|
||||
expect(imageCache.currentSize, 1);
|
||||
expect(_findPhysics(tester).velocities, <double>[0]);
|
||||
expect(_findPhysics<RecordingPhysics>(tester).velocities, <double>[0]);
|
||||
});
|
||||
|
||||
testWidgets('ScrollAwareImageProvider does not delay if in scrollable that is scrolling slowly', (WidgetTester tester) async {
|
||||
@ -119,7 +119,7 @@ void main() {
|
||||
curve: Curves.fastLinearToSlowEaseIn,
|
||||
);
|
||||
await tester.pump();
|
||||
final RecordingPhysics physics = _findPhysics(tester);
|
||||
final RecordingPhysics physics = _findPhysics<RecordingPhysics>(tester);
|
||||
|
||||
expect(physics.velocities.length, 0);
|
||||
final ImageStream stream = imageProvider.resolve(ImageConfiguration.empty);
|
||||
@ -177,7 +177,7 @@ void main() {
|
||||
curve: Curves.fastLinearToSlowEaseIn,
|
||||
);
|
||||
await tester.pump();
|
||||
final RecordingPhysics physics = _findPhysics(tester);
|
||||
final RecordingPhysics physics = _findPhysics<RecordingPhysics>(tester);
|
||||
|
||||
expect(physics.velocities.length, 0);
|
||||
final ImageStream stream = imageProvider.resolve(ImageConfiguration.empty);
|
||||
@ -245,7 +245,7 @@ void main() {
|
||||
curve: Curves.fastLinearToSlowEaseIn,
|
||||
);
|
||||
await tester.pump();
|
||||
final RecordingPhysics physics = _findPhysics(tester);
|
||||
final RecordingPhysics physics = _findPhysics<RecordingPhysics>(tester);
|
||||
|
||||
expect(physics.velocities.length, 0);
|
||||
final ImageStream stream = imageProvider.resolve(ImageConfiguration.empty);
|
||||
@ -281,6 +281,54 @@ void main() {
|
||||
|
||||
expect(imageCache.currentSize, 0);
|
||||
});
|
||||
|
||||
testWidgets('ScrollAwareImageProvider resolves from ImageCache and does not set completer twice', (WidgetTester tester) async {
|
||||
final GlobalKey<TestWidgetState> key = GlobalKey<TestWidgetState>();
|
||||
final ScrollController scrollController = ScrollController();
|
||||
await tester.pumpWidget(Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: SingleChildScrollView(
|
||||
physics: ControllablePhysics(),
|
||||
controller: scrollController,
|
||||
child: TestWidget(key),
|
||||
),
|
||||
));
|
||||
|
||||
final DisposableBuildContext context = DisposableBuildContext(key.currentState);
|
||||
const TestImage testImage = TestImage(width: 10, height: 10);
|
||||
final TestImageProvider testImageProvider = TestImageProvider(testImage);
|
||||
final ScrollAwareImageProvider<TestImageProvider> imageProvider = ScrollAwareImageProvider<TestImageProvider>(
|
||||
context: context,
|
||||
imageProvider: testImageProvider,
|
||||
);
|
||||
|
||||
expect(testImageProvider.configuration, null);
|
||||
expect(imageCache.containsKey(testImageProvider), false);
|
||||
|
||||
final ControllablePhysics physics = _findPhysics<ControllablePhysics>(tester);
|
||||
physics.recommendDeferredLoadingValue = true;
|
||||
|
||||
final ImageStream stream = imageProvider.resolve(ImageConfiguration.empty);
|
||||
|
||||
expect(testImageProvider.configuration, null);
|
||||
expect(stream.completer, null);
|
||||
expect(imageCache.containsKey(testImageProvider), false);
|
||||
expect(imageCache.currentSize, 0);
|
||||
|
||||
// Simulate a case where somone else has managed to complete this stream -
|
||||
// so it can land in the cache right before we stop scrolling fast.
|
||||
// If we miss the early return, we will fail.
|
||||
testImageProvider.complete();
|
||||
|
||||
imageCache.putIfAbsent(testImageProvider, () => testImageProvider.load(testImageProvider, PaintingBinding.instance.instantiateImageCodec));
|
||||
// We've stopped scrolling fast.
|
||||
physics.recommendDeferredLoadingValue = false;
|
||||
await tester.idle();
|
||||
|
||||
expect(imageCache.containsKey(testImageProvider), true);
|
||||
expect(imageCache.currentSize, 1);
|
||||
expect(stream.completer, null);
|
||||
});
|
||||
}
|
||||
|
||||
class TestWidget extends StatefulWidget {
|
||||
@ -311,3 +359,22 @@ class RecordingPhysics extends ScrollPhysics {
|
||||
return super.recommendDeferredLoading(velocity, metrics, context);
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore this so that we can mutate whether we defer loading or not at specific
|
||||
// times without worrying about actual scrolling mechanics.
|
||||
// ignore: must_be_immutable
|
||||
class ControllablePhysics extends ScrollPhysics {
|
||||
ControllablePhysics({ ScrollPhysics parent }) : super(parent: parent);
|
||||
|
||||
bool recommendDeferredLoadingValue = false;
|
||||
|
||||
@override
|
||||
ControllablePhysics applyTo(ScrollPhysics ancestor) {
|
||||
return ControllablePhysics(parent: buildParent(ancestor));
|
||||
}
|
||||
|
||||
@override
|
||||
bool recommendDeferredLoading(double velocity, ScrollMetrics metrics, BuildContext context) {
|
||||
return recommendDeferredLoadingValue;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user