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.
|
||||
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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user