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
This commit is contained in:
parent
8a0f9118ea
commit
b5fb6652d6
@ -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.
|
assert(_widget != null); // Use the private property to avoid a CastError during hot reload.
|
||||||
if (_dependencies != null && _dependencies!.isNotEmpty) {
|
if (_dependencies != null && _dependencies!.isNotEmpty) {
|
||||||
for (final InheritedElement dependency in _dependencies!) {
|
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
|
// 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
|
// no longer representative of what we are registered with. If we never
|
||||||
@ -6024,6 +6024,19 @@ class InheritedElement extends ProxyElement {
|
|||||||
dependent.didChangeDependencies();
|
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
|
/// Calls [Element.didChangeDependencies] of all dependent elements, if
|
||||||
/// [InheritedWidget.updateShouldNotify] returns true.
|
/// [InheritedWidget.updateShouldNotify] returns true.
|
||||||
///
|
///
|
||||||
|
@ -1696,6 +1696,56 @@ void main() {
|
|||||||
expect(states, <String>['deactivate', 'dispose']);
|
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 {
|
testWidgetsWithLeakTracking('RenderObjectElement.unmount disposes of its renderObject', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const Placeholder());
|
await tester.pumpWidget(const Placeholder());
|
||||||
final RenderObjectElement element = tester.allElements.whereType<RenderObjectElement>().last;
|
final RenderObjectElement element = tester.allElements.whereType<RenderObjectElement>().last;
|
||||||
@ -1902,12 +1952,33 @@ class DirtyElementWithCustomBuildOwner extends Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Inherited extends InheritedWidget {
|
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 int? value;
|
||||||
|
final InheritedElement Function(Inherited widget)? elementCreator;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool updateShouldNotify(Inherited oldWidget) => oldWidget.value != value;
|
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 {
|
class DependentStatefulWidget extends StatefulWidget {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user