Unverified Commit b5fb6652 authored by Alex Isaienko's avatar Alex Isaienko Committed by GitHub

InheritedElement.removeDependent() (#129210)

Call the `dependency.removeDependent(this)` instead of `dependency._dependents.remove(this)` inside the `Element.deactivate()`. This allows `InheritedElements` to know when they can release resources associated with a given dependent `Element`.

Fixes #129207
parent 8a0f9118
......@@ -4488,7 +4488,7 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
assert(_widget != null); // Use the private property to avoid a CastError during hot reload.
if (_dependencies != null && _dependencies!.isNotEmpty) {
for (final InheritedElement dependency in _dependencies!) {
dependency._dependents.remove(this);
dependency.removeDependent(this);
}
// For expediency, we don't actually clear the list here, even though it's
// no longer representative of what we are registered with. If we never
......@@ -6024,6 +6024,19 @@ class InheritedElement extends ProxyElement {
dependent.didChangeDependencies();
}
/// Called by [Element.deactivate] to remove the provided `dependent` [Element] from this [InheritedElement].
///
/// After the dependent is removed, [Element.didChangeDependencies] will no
/// longer be called on it when this [InheritedElement] notifies its dependents.
///
/// Subclasses can override this method to release any resources retained for
/// a given [dependent].
@protected
@mustCallSuper
void removeDependent(Element dependent) {
_dependents.remove(dependent);
}
/// Calls [Element.didChangeDependencies] of all dependent elements, if
/// [InheritedWidget.updateShouldNotify] returns true.
///
......
......@@ -1696,6 +1696,56 @@ void main() {
expect(states, <String>['deactivate', 'dispose']);
});
testWidgetsWithLeakTracking('Element.deactivate reports its deactivation to the InheritedElement it depends on', (WidgetTester tester) async {
final List<Key> removedDependentWidgetKeys = <Key>[];
InheritedElement elementCreator(InheritedWidget widget) {
return _InheritedElementSpy(
widget,
onRemoveDependent: (Element dependent) {
removedDependentWidgetKeys.add(dependent.widget.key!);
},
);
}
Widget builder(BuildContext context) {
context.dependOnInheritedWidgetOfExactType<Inherited>();
return Container();
}
await tester.pumpWidget(
Inherited(
0,
elementCreator: elementCreator,
child: Column(
children: <Widget>[
Builder(
key: const Key('dependent'),
builder: builder,
),
],
),
),
);
expect(removedDependentWidgetKeys, isEmpty);
await tester.pumpWidget(
Inherited(
0,
elementCreator: elementCreator,
child: Column(
children: <Widget>[
Container(),
],
),
),
);
expect(removedDependentWidgetKeys, hasLength(1));
expect(removedDependentWidgetKeys.first, const Key('dependent'));
});
testWidgetsWithLeakTracking('RenderObjectElement.unmount disposes of its renderObject', (WidgetTester tester) async {
await tester.pumpWidget(const Placeholder());
final RenderObjectElement element = tester.allElements.whereType<RenderObjectElement>().last;
......@@ -1902,12 +1952,33 @@ class DirtyElementWithCustomBuildOwner extends Element {
}
class Inherited extends InheritedWidget {
const Inherited(this.value, {super.key, required super.child});
const Inherited(this.value, {super.key, required super.child, this.elementCreator});
final int? value;
final InheritedElement Function(Inherited widget)? elementCreator;
@override
bool updateShouldNotify(Inherited oldWidget) => oldWidget.value != value;
@override
InheritedElement createElement() {
if (elementCreator != null) {
return elementCreator!(this);
}
return super.createElement();
}
}
class _InheritedElementSpy extends InheritedElement {
_InheritedElementSpy(super.widget, {this.onRemoveDependent});
final void Function(Element element)? onRemoveDependent;
@override
void removeDependent(Element dependent) {
super.removeDependent(dependent);
onRemoveDependent?.call(dependent);
}
}
class DependentStatefulWidget extends StatefulWidget {
......
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