@@ -395,6 +395,53 @@ abstract class Widget extends DiagnosticableTree {
395395 }
396396}
397397
398+ /// A cache of inherited elements with an optional parent cache.
399+ ///
400+ /// When looking up an inherited element, this will look through parent
401+ /// caches until the element is found or the end is reached. When an element
402+ /// is found, it is cached at the first element where the search began.
403+ ///
404+ /// The intention of this cache is to speed up the initial build of widget
405+ /// trees that contain a significant number of inherited widgets by deferring
406+ /// expensive map allocations until they are needed, and by only allocating
407+ /// in the "closest" hash map.
408+ ///
409+ /// This will not cache `null` results if an inherited element is not found.
410+ @visibleForTesting
411+ class InheritedTreeCache {
412+ /// Create a new [InheritedTreeCache] with an optional parent.
413+ InheritedTreeCache ([this ._parent]);
414+
415+ final InheritedTreeCache ? _parent;
416+ final HashMap <Type , InheritedElement > _current = HashMap <Type , InheritedElement >();
417+
418+ /// Place the [element] in the cache under [type] .
419+ void operator []= (Type type, InheritedElement element) {
420+ _current[type] = element;
421+ }
422+
423+ /// Find the nearest [InheritedElement] of type [type] , or `null` if it
424+ /// cannot be found.
425+ ///
426+ /// This operation will also cache the inherited element to improve the
427+ /// speed of future lookups.
428+ InheritedElement ? operator [](Type type) {
429+ InheritedElement ? potential = _current[type];
430+ if (potential != null ) {
431+ return potential;
432+ }
433+ potential = _parent? ._lookupWithoutCaching (type);
434+ if (potential != null ) {
435+ _current[type] = potential;
436+ }
437+ return potential;
438+ }
439+
440+ InheritedElement ? _lookupWithoutCaching (Type type) {
441+ return _current[type] ?? _parent? ._lookupWithoutCaching (type);
442+ }
443+ }
444+
398445/// A widget that does not require mutable state.
399446///
400447/// A stateless widget is a widget that describes part of the user interface by
@@ -3937,7 +3984,7 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
39373984 // implementation to decide whether to rebuild based on whether we had
39383985 // dependencies here.
39393986 }
3940- _inheritedWidgets = null ;
3987+ _inheritedLookup = null ;
39413988 _lifecycleState = _ElementLifecycle .inactive;
39423989 }
39433990
@@ -4129,7 +4176,8 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
41294176 return null ;
41304177 }
41314178
4132- Map <Type , InheritedElement >? _inheritedWidgets;
4179+ InheritedTreeCache ? _inheritedLookup;
4180+
41334181 Set <InheritedElement >? _dependencies;
41344182 bool _hadUnsatisfiedDependencies = false ;
41354183
@@ -4166,7 +4214,7 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
41664214 @override
41674215 T ? dependOnInheritedWidgetOfExactType <T extends InheritedWidget >({Object ? aspect}) {
41684216 assert (_debugCheckStateIsActiveForAncestorLookup ());
4169- final InheritedElement ? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets ! [T ];
4217+ final InheritedElement ? ancestor = _inheritedLookup ? [T ];
41704218 if (ancestor != null ) {
41714219 return dependOnInheritedElement (ancestor, aspect: aspect) as T ;
41724220 }
@@ -4177,13 +4225,13 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
41774225 @override
41784226 InheritedElement ? getElementForInheritedWidgetOfExactType <T extends InheritedWidget >() {
41794227 assert (_debugCheckStateIsActiveForAncestorLookup ());
4180- final InheritedElement ? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets ! [T ];
4228+ final InheritedElement ? ancestor = _inheritedLookup ? [T ];
41814229 return ancestor;
41824230 }
41834231
41844232 void _updateInheritance () {
41854233 assert (_lifecycleState == _ElementLifecycle .active);
4186- _inheritedWidgets = _parent? ._inheritedWidgets ;
4234+ _inheritedLookup = _parent? ._inheritedLookup ;
41874235 }
41884236
41894237 @override
@@ -5191,12 +5239,8 @@ class InheritedElement extends ProxyElement {
51915239 @override
51925240 void _updateInheritance () {
51935241 assert (_lifecycleState == _ElementLifecycle .active);
5194- final Map <Type , InheritedElement >? incomingWidgets = _parent? ._inheritedWidgets;
5195- if (incomingWidgets != null )
5196- _inheritedWidgets = HashMap <Type , InheritedElement >.of (incomingWidgets);
5197- else
5198- _inheritedWidgets = HashMap <Type , InheritedElement >();
5199- _inheritedWidgets! [widget.runtimeType] = this ;
5242+ _inheritedLookup = InheritedTreeCache (_parent? ._inheritedLookup)
5243+ ..[widget.runtimeType] = this ;
52005244 }
52015245
52025246 @override
0 commit comments