diff --git a/lib/web_ui/lib/src/engine/html/clip.dart b/lib/web_ui/lib/src/engine/html/clip.dart
index d2fd64fed746fded52e0b6a2d6a2238ecf377464..0cab23794627770a54689cc1a30c84f79f9acfa6 100644
--- a/lib/web_ui/lib/src/engine/html/clip.dart
+++ b/lib/web_ui/lib/src/engine/html/clip.dart
@@ -292,7 +292,9 @@ class PersistedPhysicalShape extends PersistedContainerSurface
offsetY: -pathBounds.top,
scaleX: 1.0 / pathBounds.width,
scaleY: 1.0 / pathBounds.height);
- assert(_clipElement == null);
+ // If apply is called multiple times (without update) , remove prior
+ // svg clip element.
+ _clipElement?.remove();
_clipElement =
html.Element.html(svgClipPath, treeSanitizer: _NullTreeSanitizer());
domRenderer.append(rootElement!, _clipElement!);
@@ -325,15 +327,22 @@ class PersistedPhysicalShape extends PersistedContainerSurface
}
if (oldSurface.path != path) {
oldSurface._clipElement?.remove();
+ oldSurface._clipElement = null;
+ _clipElement?.remove();
+ _clipElement = null;
// Reset style on prior element since we may have switched between
// rect/rrect and arbitrary path.
domRenderer.setElementStyle(rootElement!, 'clip-path', '');
domRenderer.setElementStyle(rootElement!, '-webkit-clip-path', '');
_applyShape();
} else {
+ // Reuse clipElement from prior surface.
_clipElement = oldSurface._clipElement;
+ if (_clipElement != null) {
+ domRenderer.append(rootElement!, _clipElement!);
+ }
+ oldSurface._clipElement = null;
}
- oldSurface._clipElement = null;
}
}
@@ -362,7 +371,8 @@ class PersistedClipPath extends PersistedContainerSurface
@override
void apply() {
_clipElement?.remove();
- final String svgClipPath = createSvgClipDef(childContainer as html.HtmlElement, clipPath);
+ final String svgClipPath =
+ createSvgClipDef(childContainer as html.HtmlElement, clipPath);
_clipElement =
html.Element.html(svgClipPath, treeSanitizer: _NullTreeSanitizer());
domRenderer.append(childContainer!, _clipElement!);
diff --git a/lib/web_ui/test/engine/surface/surface_test.dart b/lib/web_ui/test/engine/surface/surface_test.dart
index d7dbe9c827afd8a5a36c8908f411828857516957..d8c2cea4a88113aea0a829aa9273964e58ece902 100644
--- a/lib/web_ui/test/engine/surface/surface_test.dart
+++ b/lib/web_ui/test/engine/surface/surface_test.dart
@@ -359,6 +359,22 @@ void testMain() {
expect(
opacityLayer2.rootElement, element); // adopts old surface's element
});
+
+ // Regression test for https://github.com/flutter/flutter/issues/60461
+ //
+ // During retained match many to many, build can be called on existing
+ // PersistedPhysicalShape multiple times when not matched.
+ test('Can call apply multiple times on existing PersistedPhysicalShape'
+ 'when using arbitrary path',
+ () {
+ final SceneBuilder builder1 = SceneBuilder();
+ final Path path = Path();
+ path.addPolygon([Offset(50, 0), Offset(100, 80), Offset(20, 40)], true);
+ PersistedPhysicalShape shape = builder1.pushPhysicalShape(path: path,
+ color: Color(0xFF00FF00), elevation: 1);
+ builder1.build();
+ expect(() => shape.apply(), returnsNormally);
+ });
});
}