markNeedsSemanticsUpdate method

void markNeedsSemanticsUpdate()

Mark this node as needing an update to its semantics description.

This must be called whenever the semantics configuration of this RenderObject as annotated by describeSemanticsConfiguration changes in any way to update the semantics tree.

Implementation

void markNeedsSemanticsUpdate() {
  assert(!_debugDisposed);
  assert(!attached || !owner!._debugDoingSemantics);
  if (!attached || owner!._semanticsOwner == null) {
    _cachedSemanticsConfiguration = null;
    return;
  }

  // Dirty the semantics tree starting at `this` until we have reached a
  // RenderObject that is a semantics boundary. All semantics past this
  // RenderObject are still up-to date. Therefore, we will later only rebuild
  // the semantics subtree starting at the identified semantics boundary.

  final bool wasSemanticsBoundary = _semantics != null && (_cachedSemanticsConfiguration?.isSemanticBoundary ?? false);

  bool mayProduceSiblingNodes =
    _cachedSemanticsConfiguration?.childConfigurationsDelegate != null ||
    _semanticsConfiguration.childConfigurationsDelegate != null;
  _cachedSemanticsConfiguration = null;

  bool isEffectiveSemanticsBoundary = _semanticsConfiguration.isSemanticBoundary && wasSemanticsBoundary;
  RenderObject node = this;

  // The sibling nodes will be attached to the parent of immediate semantics
  // node, thus marking this semantics boundary dirty is not enough, it needs
  // to find the first parent semantics boundary that does not have any
  // possible sibling node.
  while (node.parent != null && (mayProduceSiblingNodes || !isEffectiveSemanticsBoundary)) {
    if (node != this && node._needsSemanticsUpdate) {
      break;
    }
    node._needsSemanticsUpdate = true;
    // Since this node is a semantics boundary, the produced sibling nodes will
    // be attached to the parent semantics boundary. Thus, these sibling nodes
    // will not be carried to the next loop.
    if (isEffectiveSemanticsBoundary) {
      mayProduceSiblingNodes = false;
    }

    node = node.parent!;
    isEffectiveSemanticsBoundary = node._semanticsConfiguration.isSemanticBoundary;
    if (isEffectiveSemanticsBoundary && node._semantics == null) {
      // We have reached a semantics boundary that doesn't own a semantics node.
      // That means the semantics of this branch are currently blocked and will
      // not appear in the semantics tree. We can abort the walk here.
      return;
    }
  }
  if (node != this && _semantics != null && _needsSemanticsUpdate) {
    // If `this` node has already been added to [owner._nodesNeedingSemantics]
    // remove it as it is no longer guaranteed that its semantics
    // node will continue to be in the tree. If it still is in the tree, the
    // ancestor `node` added to [owner._nodesNeedingSemantics] at the end of
    // this block will ensure that the semantics of `this` node actually gets
    // updated.
    // (See semantics_10_test.dart for an example why this is required).
    owner!._nodesNeedingSemantics.remove(this);
  }
  if (!node._needsSemanticsUpdate) {
    node._needsSemanticsUpdate = true;
    if (owner != null) {
      assert(node._semanticsConfiguration.isSemanticBoundary || node.parent == null);
      owner!._nodesNeedingSemantics.add(node);
      owner!.requestVisualUpdate();
    }
  }
}