Unverified Commit c1e24765 authored by Marcel Kirchhoff's avatar Marcel Kirchhoff Committed by GitHub

Call image stream listeners asynchronously if added asynchronously (#95525)

* Call image stream listeners with new flag

* Make _synchronousCall private

* Rename _synchronousCall parameter to be more descriptive and add more documentation

* Rename _hasInitialListeners to _addingInitialListeners and provide a better explanation

* Formatting fix

* Add tests

* Remove dependency on decoration_test.dart

* Simplify tests

* Remove empty line in comment

* Documentation

* Trigger tests

* Trigger tests

* Flip boolean value
parent d1e1c821
......@@ -340,7 +340,9 @@ class ImageStream with Diagnosticable {
if (_listeners != null) {
final List<ImageStreamListener> initialListeners = _listeners!;
_listeners = null;
_completer!._addingInitialListeners = true;
initialListeners.forEach(_completer!.addListener);
_completer!._addingInitialListeners = false;
}
}
......@@ -489,6 +491,15 @@ abstract class ImageStreamCompleter with Diagnosticable {
/// if all [keepAlive] handles get disposed.
bool _hadAtLeastOneListener = false;
/// Whether the future listeners added to this completer are initial listeners.
///
/// This can be set to true when an [ImageStream] adds its initial listeners to
/// this completer. This ultimately controls the synchronousCall parameter for
/// the listener callbacks. When adding cached listeners to a completer,
/// [_addingInitialListeners] can be set to false to indicate to the listeners
/// that they are being called asynchronously.
bool _addingInitialListeners = false;
/// Adds a listener callback that is called whenever a new concrete [ImageInfo]
/// object is available or an error is reported. If a concrete image is
/// already available, or if an error has been already reported, this object
......@@ -504,7 +515,7 @@ abstract class ImageStreamCompleter with Diagnosticable {
_listeners.add(listener);
if (_currentImage != null) {
try {
listener.onImage(_currentImage!.clone(), true);
listener.onImage(_currentImage!.clone(), !_addingInitialListeners);
} catch (exception, stack) {
reportError(
context: ErrorDescription('by a synchronously-called image listener'),
......
......@@ -6,12 +6,14 @@ import 'dart:async';
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/scheduler.dart' show timeDilation, SchedulerBinding;
import 'package:flutter_test/flutter_test.dart';
import '../image_data.dart';
import 'fake_codec.dart';
import 'mocks_for_image_cache.dart';
class FakeFrameInfo implements FrameInfo {
const FakeFrameInfo(this._duration, this._image);
......@@ -77,6 +79,24 @@ class FakeEventReportingImageStreamCompleter extends ImageStreamCompleter {
}
}
class SynchronousTestImageProvider extends ImageProvider<int> {
const SynchronousTestImageProvider(this.image);
final Image image;
@override
Future<int> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<int>(1);
}
@override
ImageStreamCompleter load(int key, DecoderCallback decode) {
return OneFrameImageStreamCompleter(
SynchronousFuture<ImageInfo>(TestImageInfo(key, image: image)),
);
}
}
void main() {
late Image image20x10;
late Image image200x100;
......@@ -838,4 +858,37 @@ void main() {
// even after evicting an image from the cache, for example.
chunkStream.add(const ImageChunkEvent(cumulativeBytesLoaded: 2, expectedTotalBytes: 3));
});
test('ImageStream, setCompleter before addListener - synchronousCall should be true', () async {
final Image image = await createTestImage(width: 100, height: 100);
final OneFrameImageStreamCompleter imageStreamCompleter =
OneFrameImageStreamCompleter(SynchronousFuture<ImageInfo>(TestImageInfo(1, image: image)));
final ImageStream imageStream = ImageStream();
imageStream.setCompleter(imageStreamCompleter);
bool? synchronouslyCalled;
imageStream.addListener(ImageStreamListener((ImageInfo image, bool synchronousCall) {
synchronouslyCalled = synchronousCall;
}));
expect(synchronouslyCalled, true);
});
test('ImageStream, setCompleter after addListener - synchronousCall should be false', () async {
final Image image = await createTestImage(width: 100, height: 100);
final OneFrameImageStreamCompleter imageStreamCompleter =
OneFrameImageStreamCompleter(SynchronousFuture<ImageInfo>(TestImageInfo(1, image: image)));
final ImageStream imageStream = ImageStream();
bool? synchronouslyCalled;
imageStream.addListener(ImageStreamListener((ImageInfo image, bool synchronousCall) {
synchronouslyCalled = synchronousCall;
}));
imageStream.setCompleter(imageStreamCompleter);
expect(synchronouslyCalled, false);
});
}
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