Skip to content

Commit ed698b8

Browse files
author
Hans Muller
authored
Remove inheritFromElement assert, it can fail during deactivation (flutter#21570)
1 parent 6effa19 commit ed698b8

File tree

5 files changed

+806
-17
lines changed

5 files changed

+806
-17
lines changed

packages/flutter/lib/src/widgets/framework.dart

Lines changed: 147 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1870,6 +1870,19 @@ abstract class BuildContext {
18701870
/// render object is usually short.
18711871
Size get size;
18721872

1873+
/// Registers this build context with [ancestor] such that when
1874+
/// [ancestor]'s widget changes this build context is rebuilt.
1875+
///
1876+
/// Returns `ancestor.widget`.
1877+
///
1878+
/// This method is rarely called directly. Most applications should use
1879+
/// [inheritFromWidgetOfExactType], which calls this method after finding
1880+
/// the appropriate [InheritedElement] ancestor.
1881+
///
1882+
/// All of the qualifications about when [inheritFromWidgetOfExactType] can
1883+
/// be called apply to this method as well.
1884+
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect });
1885+
18731886
/// Obtains the nearest widget of the given type, which must be the type of a
18741887
/// concrete [InheritedWidget] subclass, and registers this build context with
18751888
/// that widget such that when that widget changes (or a new widget of that
@@ -1879,7 +1892,7 @@ abstract class BuildContext {
18791892
/// This is typically called implicitly from `of()` static methods, e.g.
18801893
/// [Theme.of].
18811894
///
1882-
/// This should not be called from widget constructors or from
1895+
/// This method should not be called from widget constructors or from
18831896
/// [State.initState] methods, because those methods would not get called
18841897
/// again if the inherited value were to change. To ensure that the widget
18851898
/// correctly updates itself when the inherited value changes, only call this
@@ -1892,9 +1905,9 @@ abstract class BuildContext {
18921905
/// It is safe to use this method from [State.deactivate], which is called
18931906
/// whenever the widget is removed from the tree.
18941907
///
1895-
/// It is also possible to call this from interaction event handlers (e.g.
1896-
/// gesture callbacks) or timers, to obtain a value once, if that value is not
1897-
/// going to be cached and reused later.
1908+
/// It is also possible to call this method from interaction event handlers
1909+
/// (e.g. gesture callbacks) or timers, to obtain a value once, if that value
1910+
/// is not going to be cached and reused later.
18981911
///
18991912
/// Calling this method is O(1) with a small constant factor, but will lead to
19001913
/// the widget being rebuilt more often.
@@ -1904,7 +1917,12 @@ abstract class BuildContext {
19041917
/// called, whenever changes occur relating to that widget until the next time
19051918
/// the widget or one of its ancestors is moved (for example, because an
19061919
/// ancestor is added or removed).
1907-
InheritedWidget inheritFromWidgetOfExactType(Type targetType);
1920+
///
1921+
/// The [aspect] parameter is only used when [targetType] is an
1922+
/// [InheritedWidget] subclasses that supports partial updates, like
1923+
/// [InheritedModel]. It specifies what "aspect" of the inherited
1924+
/// widget this context depends on.
1925+
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect });
19081926

19091927
/// Obtains the element corresponding to the nearest widget of the given type,
19101928
/// which must be the type of a concrete [InheritedWidget] subclass.
@@ -3226,15 +3244,21 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
32263244
}
32273245

32283246
@override
3229-
InheritedWidget inheritFromWidgetOfExactType(Type targetType) {
3247+
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
3248+
assert(ancestor != null);
3249+
_dependencies ??= new HashSet<InheritedElement>();
3250+
_dependencies.add(ancestor);
3251+
ancestor.updateDependencies(this, aspect);
3252+
return ancestor.widget;
3253+
}
3254+
3255+
@override
3256+
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
32303257
assert(_debugCheckStateIsActiveForAncestorLookup());
32313258
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
32323259
if (ancestor != null) {
32333260
assert(ancestor is InheritedElement);
3234-
_dependencies ??= new HashSet<InheritedElement>();
3235-
_dependencies.add(ancestor);
3236-
ancestor._dependents.add(this);
3237-
return ancestor.widget;
3261+
return inheritFromElement(ancestor, aspect: aspect);
32383262
}
32393263
_hadUnsatisfiedDependencies = true;
32403264
return null;
@@ -3845,11 +3869,13 @@ class StatefulElement extends ComponentElement {
38453869
}
38463870

38473871
@override
3848-
InheritedWidget inheritFromWidgetOfExactType(Type targetType) {
3872+
InheritedWidget inheritFromElement(Element ancestor, { Object aspect }) {
3873+
assert(ancestor != null);
38493874
assert(() {
3875+
final Type targetType = ancestor.widget.runtimeType;
38503876
if (state._debugLifecycleState == _StateLifecycle.created) {
38513877
throw new FlutterError(
3852-
'inheritFromWidgetOfExactType($targetType) was called before ${_state.runtimeType}.initState() completed.\n'
3878+
'inheritFromWidgetOfExactType($targetType) or inheritFromElement() was called before ${_state.runtimeType}.initState() completed.\n'
38533879
'When an inherited widget changes, for example if the value of Theme.of() changes, '
38543880
'its dependent widgets are rebuilt. If the dependent widget\'s reference to '
38553881
'the inherited widget is in a constructor or an initState() method, '
@@ -3862,7 +3888,7 @@ class StatefulElement extends ComponentElement {
38623888
}
38633889
if (state._debugLifecycleState == _StateLifecycle.defunct) {
38643890
throw new FlutterError(
3865-
'inheritFromWidgetOfExactType($targetType) called after dispose(): $this\n'
3891+
'inheritFromWidgetOfExactType($targetType) or inheritFromElement() was called after dispose(): $this\n'
38663892
'This error happens if you call inheritFromWidgetOfExactType() on the '
38673893
'BuildContext for a widget that no longer appears in the widget tree '
38683894
'(e.g., whose parent widget no longer includes the widget in its '
@@ -3882,7 +3908,7 @@ class StatefulElement extends ComponentElement {
38823908
}
38833909
return true;
38843910
}());
3885-
return super.inheritFromWidgetOfExactType(targetType);
3911+
return super.inheritFromElement(ancestor, aspect: aspect);
38863912
}
38873913

38883914
@override
@@ -4032,7 +4058,7 @@ class InheritedElement extends ProxyElement {
40324058
@override
40334059
InheritedWidget get widget => super.widget;
40344060

4035-
final Set<Element> _dependents = new HashSet<Element>();
4061+
final Map<Element, Object> _dependents = new HashMap<Element, Object>();
40364062

40374063
@override
40384064
void _updateInheritance() {
@@ -4054,6 +4080,110 @@ class InheritedElement extends ProxyElement {
40544080
super.debugDeactivated();
40554081
}
40564082

4083+
/// Returns the dependencies value recorded for [dependent]
4084+
/// with [setDependencies].
4085+
///
4086+
/// Each dependent element is mapped to a single object value
4087+
/// which represents how the element depends on this
4088+
/// [InheritedElement]. This value is null by default and by default
4089+
/// dependent elements are rebuilt unconditionally.
4090+
///
4091+
/// Subclasses can manage these values with [updateDependencies]
4092+
/// so that they can selectively rebuild dependents in
4093+
/// [notifyDependents].
4094+
///
4095+
/// This method is typically only called in overrides of [updateDependencies].
4096+
///
4097+
/// See also:
4098+
///
4099+
/// * [updateDependencies], which is called each time a dependency is
4100+
/// created with [inheritFromWidgetOfExactType].
4101+
/// * [setDependencies], which sets dependencies value for a dependent
4102+
/// element.
4103+
/// * [notifyDependent], which can be overridden to use a dependent's
4104+
/// dependencies value to decide if the dependent needs to be rebuilt.
4105+
/// * [InheritedModel], which is an example of a class that uses this method
4106+
/// to manage dependency values.
4107+
@protected
4108+
Object getDependencies(Element dependent) {
4109+
return _dependents[dependent];
4110+
}
4111+
4112+
/// Sets the value returned by [getDependencies] value for [dependent].
4113+
///
4114+
/// Each dependent element is mapped to a single object value
4115+
/// which represents how the element depends on this
4116+
/// [InheritedElement]. The [updateDependencies] method sets this value to
4117+
/// null by default so that dependent elements are rebuilt unconditionally.
4118+
///
4119+
/// Subclasses can manage these values with [updateDependencies]
4120+
/// so that they can selectively rebuild dependents in [notifyDependents].
4121+
///
4122+
/// This method is typically only called in overrides of [updateDependencies].
4123+
///
4124+
/// See also:
4125+
///
4126+
/// * [updateDependencies], which is called each time a dependency is
4127+
/// created with [inheritFromWidgetOfExactType].
4128+
/// * [getDependencies], which returns the current value for a dependent
4129+
/// element.
4130+
/// * [notifyDependent], which can be overridden to use a dependent's
4131+
/// [getDependencies] value to decide if the dependent needs to be rebuilt.
4132+
/// * [InheritedModel], which is an example of a class that uses this method
4133+
/// to manage dependency values.
4134+
@protected
4135+
void setDependencies(Element dependent, Object value) {
4136+
_dependents[dependent] = value;
4137+
}
4138+
4139+
/// Called by [inheritFromWidgetOfExactType] when a new [dependent] is added.
4140+
///
4141+
/// Each dependent element can be mapped to a single object value with
4142+
/// [setDependencies]. This method can lookup the existing dependencies with
4143+
/// [getDependencies].
4144+
///
4145+
/// By default this method sets the inherited dependencies for [dependent]
4146+
/// to null. This only serves to record an unconditional dependency on
4147+
/// [dependent].
4148+
///
4149+
/// Subclasses can manage their own dependencies values so that they
4150+
/// can selectively rebuild dependents in [notifyDependents].
4151+
///
4152+
/// See also:
4153+
///
4154+
/// * [getDependencies], which returns the current value for a dependent
4155+
/// element.
4156+
/// * [setDependencies], which sets the value for a dependent element.
4157+
/// * [notifyDependent], which can be overridden to use a dependent's
4158+
/// dependencies value to decide if the dependent needs to be rebuilt.
4159+
/// * [InheritedModel], which is an example of a class that uses this method
4160+
/// to manage dependency values.
4161+
@protected
4162+
void updateDependencies(Element dependent, Object aspect) {
4163+
setDependencies(dependent, null);
4164+
}
4165+
4166+
/// Called by [notifyClients] for each dependent.
4167+
///
4168+
/// Calls `dependent.didChangeDependencies()` by default.
4169+
///
4170+
/// Subclasses can override this method to selectively call
4171+
/// [didChangeDependencies] based on the value of [getDependencies].
4172+
///
4173+
/// See also:
4174+
///
4175+
/// * [updateDependencies], which is called each time a dependency is
4176+
/// created with [inheritFromWidgetOfExactType].
4177+
/// * [getDependencies], which returns the current value for a dependent
4178+
/// element.
4179+
/// * [setDependencies], which sets the value for a dependent element.
4180+
/// * [InheritedModel], which is an example of a class that uses this method
4181+
/// to manage dependency values.
4182+
@protected
4183+
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
4184+
dependent.didChangeDependencies();
4185+
}
4186+
40574187
/// Calls [Element.didChangeDependencies] of all dependent elements, if
40584188
/// [InheritedWidget.updateShouldNotify] returns true.
40594189
///
@@ -4070,7 +4200,7 @@ class InheritedElement extends ProxyElement {
40704200
if (!widget.updateShouldNotify(oldWidget))
40714201
return;
40724202
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
4073-
for (Element dependent in _dependents) {
4203+
for (Element dependent in _dependents.keys) {
40744204
assert(() {
40754205
// check that it really is our descendant
40764206
Element ancestor = dependent._parent;
@@ -4080,7 +4210,7 @@ class InheritedElement extends ProxyElement {
40804210
}());
40814211
// check that it really depends on us
40824212
assert(dependent._dependencies.contains(this));
4083-
dependent.didChangeDependencies();
4213+
notifyDependent(oldWidget, dependent);
40844214
}
40854215
}
40864216
}

0 commit comments

Comments
 (0)