@@ -1383,33 +1383,6 @@ class SemanticsObject {
13831383/// The dom element of this semantics object.
13841384DomElement get element=> semanticRole! .element;
13851385
1386- /// Returns the HTML element that contains the HTML elements of direct
1387- /// children of this object.
1388- ///
1389- /// The element is created lazily. When the child list is empty this element
1390- /// is not created. This is necessary for "aria-label" to function correctly.
1391- /// The browser will ignore the[label] of HTML element that contain child
1392- /// elements.
1393- DomElement ? getOrCreateChildContainer () {
1394- if (_childContainerElement== null ) {
1395- _childContainerElement= createDomElement ('flt-semantics-container' );
1396- _childContainerElement! .style
1397- ..position= 'absolute'
1398- // Ignore pointer events on child container so that platform views
1399- // behind it can be reached.
1400- ..pointerEvents= 'none' ;
1401- element.append (_childContainerElement! );
1402- }
1403- return _childContainerElement;
1404- }
1405-
1406- /// The element that contains the elements belonging to the child semantics
1407- /// nodes.
1408- ///
1409- /// This element is used to correct for[_rect] offsets. It is only non-`null`
1410- /// when there are non-zero children (i.e. when[hasChildren] is`true` ).
1411- DomElement ? _childContainerElement;
1412-
14131386/// The parent of this semantics object.
14141387 ///
14151388 /// This value is not final until the tree is finalized. It is not safe to
@@ -1677,12 +1650,6 @@ class SemanticsObject {
16771650// Apply updates to the DOM.
16781651_updateRole ();
16791652
1680- // All properties that affect positioning and sizing are checked together
1681- // any one of them triggers position and size recomputation.
1682- if (isRectDirty|| isTransformDirty|| isScrollPositionDirty) {
1683- recomputePositionAndSize ();
1684- }
1685-
16861653if (semanticRole! .acceptsPointerEvents) {
16871654 element.style.pointerEvents= 'all' ;
16881655 }else {
@@ -1710,22 +1677,15 @@ class SemanticsObject {
17101677// Trivial case: remove all children.
17111678if (_childrenInHitTestOrder== null || _childrenInHitTestOrder! .isEmpty) {
17121679if (_currentChildrenInRenderOrder== null || _currentChildrenInRenderOrder! .isEmpty) {
1713- // A container element must not have been created when child list is empty.
1714- assert (_childContainerElement== null );
17151680 _currentChildrenInRenderOrder= null ;
17161681return ;
17171682 }
17181683
1719- // A container element must have been created when child list is not empty.
1720- assert (_childContainerElement!= null );
1721-
17221684// Remove all children from this semantics object.
17231685final int len= _currentChildrenInRenderOrder! .length;
17241686for (int i= 0 ; i< len; i++ ) {
17251687 owner._detachObject (_currentChildrenInRenderOrder! [i].id);
17261688 }
1727- _childContainerElement! .remove ();
1728- _childContainerElement= null ;
17291689 _currentChildrenInRenderOrder= null ;
17301690return ;
17311691 }
@@ -1734,7 +1694,6 @@ class SemanticsObject {
17341694final Int32List childrenInTraversalOrder= _childrenInTraversalOrder! ;
17351695final Int32List childrenInHitTestOrder= _childrenInHitTestOrder! ;
17361696final int childCount= childrenInHitTestOrder.length;
1737- final DomElement ? containerElement= getOrCreateChildContainer ();
17381697
17391698assert (childrenInTraversalOrder.length== childrenInHitTestOrder.length);
17401699
@@ -1766,7 +1725,7 @@ class SemanticsObject {
17661725// Trivial case: previous list was empty => just populate the container.
17671726if (_currentChildrenInRenderOrder== null || _currentChildrenInRenderOrder! .isEmpty) {
17681727for (final SemanticsObject childin childrenInRenderOrder) {
1769- containerElement ! .append (child.element);
1728+ element .append (child.element);
17701729 owner._attachObject (parent: this , child: child);
17711730 }
17721731 _currentChildrenInRenderOrder= childrenInRenderOrder;
@@ -1850,9 +1809,9 @@ class SemanticsObject {
18501809final SemanticsObject child= childrenInRenderOrder[i];
18511810if (! stationaryIds.contains (child.id)) {
18521811if (refNode== null ) {
1853- containerElement ! .append (child.element);
1812+ element .append (child.element);
18541813 }else {
1855- containerElement ! .insertBefore (child.element, refNode);
1814+ element .insertBefore (child.element, refNode);
18561815 }
18571816 owner._attachObject (parent: this , child: child);
18581817 }else {
@@ -2030,9 +1989,10 @@ class SemanticsObject {
20301989
20311990// Reparent element.
20321991if (previousElement!= element) {
2033- final DomElement ? container= _childContainerElement;
2034- if (container!= null ) {
2035- element.append (container);
1992+ if (_currentChildrenInRenderOrder!= null ) {
1993+ for (final childin _currentChildrenInRenderOrder! ) {
1994+ element.append (child.element);
1995+ }
20361996 }
20371997final DomElement ? parent= previousElement? .parent;
20381998if (parent!= null ) {
@@ -2127,60 +2087,79 @@ class SemanticsObject {
21272087/// Indicates whether the node is currently expanded.
21282088bool get isExpanded=> hasFlag (ui.SemanticsFlag .isExpanded);
21292089
2130- /// Role-specific adjustment of the vertical position of thechild container .
2090+ /// Role-specific adjustment of the vertical position of thechildren .
21312091 ///
21322092 /// This is used, for example, by the[SemanticScrollable] to compensate for the
21332093 ///`scrollTop` offset in the DOM.
21342094 ///
21352095 /// This field must not be null.
2136- double verticalContainerAdjustment = 0.0 ;
2096+ double verticalScrollAdjustment = 0.0 ;
21372097
2138- /// Role-specific adjustment of the horizontal position of the child
2139- /// container.
2098+ /// Role-specific adjustment of the horizontal position of children.
21402099 ///
21412100 /// This is used, for example, by the[SemanticScrollable] to compensate for the
21422101 ///`scrollLeft` offset in the DOM.
21432102 ///
21442103 /// This field must not be null.
2145- double horizontalContainerAdjustment= 0.0 ;
2104+ double horizontalScrollAdjustment= 0.0 ;
2105+
2106+ double verticalAdjustmentFromParent= 0.0 ;
2107+ double horizontalAdjustmentFromParent= 0.0 ;
2108+
2109+ /// If this element[hasChildren] , computes the parent adjustment for each child.
2110+ void recomputeChildrenAdjustment (Set <SemanticsObject > dirtyNodes) {
2111+ if (! hasChildren) {
2112+ return ;
2113+ }
2114+ // If this node has children, we need to compensate for the parent's rect and
2115+ // pass down the scroll adjustments.
2116+ final double translateX= - _rect! .left+ horizontalScrollAdjustment;
2117+ final double translateY= - _rect! .top+ verticalScrollAdjustment;
2118+
2119+ for (final childIndexin _childrenInTraversalOrder! ) {
2120+ final child= owner._semanticsTree[childIndex]! ;
2121+
2122+ if (child.horizontalAdjustmentFromParent!= translateX||
2123+ child.verticalAdjustmentFromParent!= translateY) {
2124+ child.horizontalAdjustmentFromParent= translateX;
2125+ child.verticalAdjustmentFromParent= translateY;
2126+ dirtyNodes.add (child);
2127+ }
2128+ }
2129+ }
21462130
2147- /// Computes the size and position of[element] and, if this element
2148- ///[hasChildren] , of[getOrCreateChildContainer] .
2131+ /// Computes the size and position of[element]
21492132void recomputePositionAndSize () {
21502133 element.style
21512134 ..width= '${_rect !.width }px'
21522135 ..height= '${_rect !.height }px' ;
21532136
2154- final DomElement ? containerElement= hasChildren? getOrCreateChildContainer (): null ;
2155-
21562137final bool hasZeroRectOffset= _rect! .top== 0.0 && _rect! .left== 0.0 ;
21572138final Float32List ? transform= _transform;
21582139final bool hasIdentityTransform=
21592140 transform== null || isIdentityFloat32ListTransform (transform);
21602141
21612142if (hasZeroRectOffset&&
21622143 hasIdentityTransform&&
2163- verticalContainerAdjustment == 0.0 &&
2164- horizontalContainerAdjustment == 0.0 ) {
2144+ verticalAdjustmentFromParent == 0.0 &&
2145+ horizontalAdjustmentFromParent == 0.0 ) {
21652146_clearSemanticElementTransform (element);
2166- if (containerElement!= null ) {
2167- _clearSemanticElementTransform (containerElement);
2168- }
21692147return ;
21702148 }
21712149
21722150late Matrix4 effectiveTransform;
21732151bool effectiveTransformIsIdentity= true ;
2174- if (! hasZeroRectOffset) {
2152+
2153+ final double left= _rect! .left+ horizontalAdjustmentFromParent;
2154+ final double top= _rect! .top+ verticalAdjustmentFromParent;
2155+
2156+ if (left!= 0.0 || top!= 0.0 ) {
21752157if (transform== null ) {
2176- final double left= _rect! .left;
2177- final double top= _rect! .top;
21782158 effectiveTransform= Matrix4 .translationValues (left, top,0.0 );
2179- effectiveTransformIsIdentity= left == 0.0 && top == 0.0 ;
2159+ effectiveTransformIsIdentity= false ;
21802160 }else {
21812161// Clone to avoid mutating _transform.
2182- effectiveTransform=
2183- Matrix4 .fromFloat32List (transform).clone ()..translate (_rect! .left, _rect! .top);
2162+ effectiveTransform= Matrix4 .fromFloat32List (transform).clone ()..translate (left, top);
21842163 effectiveTransformIsIdentity= effectiveTransform.isIdentity ();
21852164 }
21862165 }else if (! hasIdentityTransform) {
@@ -2195,19 +2174,15 @@ class SemanticsObject {
21952174 }else {
21962175_clearSemanticElementTransform (element);
21972176 }
2177+ }
21982178
2199- if (containerElement!= null ) {
2200- if (! hasZeroRectOffset||
2201- verticalContainerAdjustment!= 0.0 ||
2202- horizontalContainerAdjustment!= 0.0 ) {
2203- final double translateX= - _rect! .left+ horizontalContainerAdjustment;
2204- final double translateY= - _rect! .top+ verticalContainerAdjustment;
2205- containerElement.style
2206- ..top= '${translateY }px'
2207- ..left= '${translateX }px' ;
2208- }else {
2209- _clearSemanticElementTransform (containerElement);
2210- }
2179+ /// Computes the size and position of children.
2180+ void updateChildrenPositionAndSize () {
2181+ final Set <SemanticsObject > dirtyNodes= < SemanticsObject > {};
2182+ recomputeChildrenAdjustment (dirtyNodes);
2183+
2184+ for (final nodein dirtyNodes) {
2185+ node.recomputePositionAndSize ();
22112186 }
22122187 }
22132188
@@ -2769,7 +2744,7 @@ class EngineSemanticsOwner {
27692744 removals.add (node);
27702745 }else {
27712746assert (node._parent== parent);
2772- assert (node.element.parentNode== parent._childContainerElement );
2747+ assert (node.element.parentNode== parent.element );
27732748 }
27742749return true ;
27752750 });
@@ -2872,14 +2847,29 @@ class EngineSemanticsOwner {
28722847 object.updateSelf (nodeUpdate);
28732848 }
28742849
2850+ final Set <SemanticsObject > nodesWithDirtyPositionsAndSizes= < SemanticsObject > {};
28752851// Second, fix the tree structure. This is moved out into its own loop,
28762852// because each object's own information must be updated first.
28772853for (final SemanticsNodeUpdate nodeUpdatein nodeUpdates) {
28782854final SemanticsObject object= _semanticsTree[nodeUpdate.id]! ;
28792855 object.updateChildren ();
2856+
2857+ if (object.isRectDirty||
2858+ object.isTransformDirty||
2859+ object.isScrollPositionDirty||
2860+ object.isChildrenInTraversalOrderDirty) {
2861+ nodesWithDirtyPositionsAndSizes.add (object);
2862+
2863+ object.recomputeChildrenAdjustment (nodesWithDirtyPositionsAndSizes);
2864+ }
2865+
28802866 object._dirtyFields= 0 ;
28812867 }
28822868
2869+ for (final nodein nodesWithDirtyPositionsAndSizes) {
2870+ node.recomputePositionAndSize ();
2871+ }
2872+
28832873final SemanticsObject root= _semanticsTree[0 ]! ;
28842874if (_rootSemanticsElement== null ) {
28852875 _rootSemanticsElement= root.element;
@@ -2913,9 +2903,6 @@ AFTER: $description
29132903// Dirty fields should be cleared after the tree has been finalized.
29142904assert (object._dirtyFields== 0 );
29152905
2916- // Make sure a child container is created only when there are children.
2917- assert (object._childContainerElement== null || object.hasChildren);
2918-
29192906// Ensure child ID list is consistent with the parent-child
29202907// relationship of the semantics tree.
29212908if (object._childrenInTraversalOrder!= null ) {