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); + }); }); }