diff --git a/super_editor/lib/src/infrastructure/content_layers.dart b/super_editor/lib/src/infrastructure/content_layers.dart index 9e54d3231a..6360161169 100644 --- a/super_editor/lib/src/infrastructure/content_layers.dart +++ b/super_editor/lib/src/infrastructure/content_layers.dart @@ -267,30 +267,45 @@ class ContentLayersElement extends RenderObjectElement { contentLayersLog.finer("ContentLayersElement - (re)building layers"); owner!.buildScope(this, () { - final List underlays = List.filled(widget.underlays.length, _NullElement.instance); - for (int i = 0; i < underlays.length; i += 1) { - late final Element child; - if (i > _underlays.length - 1) { - child = inflateWidget(widget.underlays[i](this), _UnderlaySlot(i)); - } else { - child = super.updateChild(_underlays[i], widget.underlays[i](this), _UnderlaySlot(i))!; - } - underlays[i] = child; + _buildLayersWithExistingScope(); + }); + } + + /// Builds the underlays and overlays without establishing a new build scope. + /// + /// We build the layers in two situations: + /// + /// 1. When the content's layout is dirty. This happens during layout phase, when we need to + /// establish a build scope. This is done when [buildLayers] is called. + /// 2. When the content's layout is clean. This happens when [update] is called, but only + /// non-layout changes happened, like changing a color. In this case, we are already + /// inside a build scope, so we can't try to establish a new one. + /// + /// See [BuildOwner.buildScope] for more information. + void _buildLayersWithExistingScope() { + final List underlays = List.filled(widget.underlays.length, _NullElement.instance); + for (int i = 0; i < underlays.length; i += 1) { + late final Element child; + if (i > _underlays.length - 1) { + child = inflateWidget(widget.underlays[i](this), _UnderlaySlot(i)); + } else { + child = super.updateChild(_underlays[i], widget.underlays[i](this), _UnderlaySlot(i))!; } - _underlays = underlays; - - final List overlays = List.filled(widget.overlays.length, _NullElement.instance); - for (int i = 0; i < overlays.length; i += 1) { - late final Element child; - if (i > _overlays.length - 1) { - child = inflateWidget(widget.overlays[i](this), _OverlaySlot(i)); - } else { - child = super.updateChild(_overlays[i], widget.overlays[i](this), _OverlaySlot(i))!; - } - overlays[i] = child; + underlays[i] = child; + } + _underlays = underlays; + + final List overlays = List.filled(widget.overlays.length, _NullElement.instance); + for (int i = 0; i < overlays.length; i += 1) { + late final Element child; + if (i > _overlays.length - 1) { + child = inflateWidget(widget.overlays[i](this), _OverlaySlot(i)); + } else { + child = super.updateChild(_overlays[i], widget.overlays[i](this), _OverlaySlot(i))!; } - _overlays = overlays; - }); + overlays[i] = child; + } + _overlays = overlays; } @override @@ -331,6 +346,15 @@ class ContentLayersElement extends RenderObjectElement { assert(!debugChildrenHaveDuplicateKeys(widget, [newContent])); _content = updateChild(_content, newContent, _contentSlot); + + if (!renderObject.contentNeedsLayout) { + // Layout has already run. No layout bounds changed. There might be a + // non-layout change that needs to be painted, e.g., change to theme brightness. + // Re-build all layers, which is safe to do because no layout constraints changed. + _buildLayersWithExistingScope(); + } + // Else, dirty content layout will cause this whole widget to re-layout. The + // layers will be re-built during that layout pass. } @override