Enable ChangeNotifier clients to dispatch event of object creation in constructor. (#133060)
This commit is contained in:
parent
382ceb5707
commit
89907f6da3
@ -207,6 +207,39 @@ mixin class ChangeNotifier implements Listenable {
|
|||||||
@protected
|
@protected
|
||||||
bool get hasListeners => _count > 0;
|
bool get hasListeners => _count > 0;
|
||||||
|
|
||||||
|
/// Dispatches event of object creation to [MemoryAllocations.instance].
|
||||||
|
///
|
||||||
|
/// If the event was already dispatched or [kFlutterMemoryAllocationsEnabled]
|
||||||
|
/// is false, the method is noop.
|
||||||
|
///
|
||||||
|
/// Tools like leak_tracker use the event of object creation to help
|
||||||
|
/// developers identify the owner of the object, for troubleshooting purposes,
|
||||||
|
/// by taking stack trace at the moment of the event.
|
||||||
|
///
|
||||||
|
/// But, as [ChangeNotifier] is mixin, it does not have its own constructor. So, it
|
||||||
|
/// communicates object creation in first `addListener`, that results
|
||||||
|
/// in the stack trace pointing to `addListener`, not to constructor.
|
||||||
|
///
|
||||||
|
/// To make debugging easier, invoke [ChangeNotifier.maybeDispatchObjectCreation]
|
||||||
|
/// in constructor of the class. It will help
|
||||||
|
/// to identify the owner.
|
||||||
|
///
|
||||||
|
/// Make sure to invoke it with condition `if (kFlutterMemoryAllocationsEnabled) ...`
|
||||||
|
/// so that the method is tree-shaken away when the flag is false.
|
||||||
|
@protected
|
||||||
|
void maybeDispatchObjectCreation() {
|
||||||
|
// Tree shaker does not include this method and the class MemoryAllocations
|
||||||
|
// if kFlutterMemoryAllocationsEnabled is false.
|
||||||
|
if (kFlutterMemoryAllocationsEnabled && !_creationDispatched) {
|
||||||
|
MemoryAllocations.instance.dispatchObjectCreated(
|
||||||
|
library: _flutterFoundationLibrary,
|
||||||
|
className: '$ChangeNotifier',
|
||||||
|
object: this,
|
||||||
|
);
|
||||||
|
_creationDispatched = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Register a closure to be called when the object changes.
|
/// Register a closure to be called when the object changes.
|
||||||
///
|
///
|
||||||
/// If the given closure is already registered, an additional instance is
|
/// If the given closure is already registered, an additional instance is
|
||||||
@ -236,14 +269,11 @@ mixin class ChangeNotifier implements Listenable {
|
|||||||
@override
|
@override
|
||||||
void addListener(VoidCallback listener) {
|
void addListener(VoidCallback listener) {
|
||||||
assert(ChangeNotifier.debugAssertNotDisposed(this));
|
assert(ChangeNotifier.debugAssertNotDisposed(this));
|
||||||
if (kFlutterMemoryAllocationsEnabled && !_creationDispatched) {
|
|
||||||
MemoryAllocations.instance.dispatchObjectCreated(
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
library: _flutterFoundationLibrary,
|
maybeDispatchObjectCreation();
|
||||||
className: '$ChangeNotifier',
|
|
||||||
object: this,
|
|
||||||
);
|
|
||||||
_creationDispatched = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_count == _listeners.length) {
|
if (_count == _listeners.length) {
|
||||||
if (_count == 0) {
|
if (_count == 0) {
|
||||||
_listeners = List<VoidCallback?>.filled(1, null);
|
_listeners = List<VoidCallback?>.filled(1, null);
|
||||||
@ -505,13 +535,8 @@ class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {
|
|||||||
/// Creates a [ChangeNotifier] that wraps this value.
|
/// Creates a [ChangeNotifier] that wraps this value.
|
||||||
ValueNotifier(this._value) {
|
ValueNotifier(this._value) {
|
||||||
if (kFlutterMemoryAllocationsEnabled) {
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
MemoryAllocations.instance.dispatchObjectCreated(
|
maybeDispatchObjectCreation();
|
||||||
library: _flutterFoundationLibrary,
|
|
||||||
className: '$ValueNotifier',
|
|
||||||
object: this,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_creationDispatched = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current value stored in this notifier.
|
/// The current value stored in this notifier.
|
||||||
|
@ -1196,6 +1196,13 @@ class ShortcutRegistryEntry {
|
|||||||
/// widgets that are not descendants of the registry can listen to it (e.g. in
|
/// widgets that are not descendants of the registry can listen to it (e.g. in
|
||||||
/// overlays).
|
/// overlays).
|
||||||
class ShortcutRegistry with ChangeNotifier {
|
class ShortcutRegistry with ChangeNotifier {
|
||||||
|
/// Creates an instance of [ShortcutRegistry].
|
||||||
|
ShortcutRegistry() {
|
||||||
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
|
maybeDispatchObjectCreation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool _notificationScheduled = false;
|
bool _notificationScheduled = false;
|
||||||
bool _disposed = false;
|
bool _disposed = false;
|
||||||
|
|
||||||
|
@ -482,4 +482,8 @@ class _DefaultSnapshotPainter implements SnapshotPainter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldRepaint(covariant _DefaultSnapshotPainter oldPainter) => false;
|
bool shouldRepaint(covariant _DefaultSnapshotPainter oldPainter) => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@protected
|
||||||
|
void maybeDispatchObjectCreation() { }
|
||||||
}
|
}
|
||||||
|
@ -1852,6 +1852,22 @@ void main() {
|
|||||||
}, throwsAssertionError);
|
}, throwsAssertionError);
|
||||||
token.dispose();
|
token.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('dispatches object creation in constructor', (WidgetTester tester) async {
|
||||||
|
final MemoryAllocations ma = MemoryAllocations.instance;
|
||||||
|
assert(!ma.hasListeners);
|
||||||
|
int eventCount = 0;
|
||||||
|
void listener(ObjectEvent event) => eventCount++;
|
||||||
|
ma.addListener(listener);
|
||||||
|
|
||||||
|
final ShortcutRegistry registry = ShortcutRegistry();
|
||||||
|
|
||||||
|
expect(eventCount, 1);
|
||||||
|
|
||||||
|
registry.dispose();
|
||||||
|
ma.removeListener(listener);
|
||||||
|
assert(!ma.hasListeners);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user