flushSemantics method

void flushSemantics()

Update the semantics for render objects marked as needing a semantics update.

Initially, only the root node, as scheduled by RenderObject.scheduleInitialSemantics, needs a semantics update.

This function is one of the core stages of the rendering pipeline. The semantics are compiled after painting and only after RenderObject.scheduleInitialSemantics has been called.

See RendererBinding for an example of how this function is used.

Implementation

// See [_RenderObjectSemantics]'s documentation for detailed explanations on
// what this method does
void flushSemantics() {
  if (_semanticsOwner == null) {
    return;
  }
  if (!kReleaseMode) {
    FlutterTimeline.startSync('SEMANTICS$_debugRootSuffixForTimelineEventNames');
  }
  assert(_semanticsOwner != null);
  assert(() {
    _debugDoingSemantics = true;
    return true;
  }());
  try {
    // This has to be top-to-down order since the geometries of a child and its
    // subtree depends on ancestors' transforms and clips. If it updates child
    // first, it may use dirty geometry in parent's semantics node to
    // calculate the geometries in the subtree.
    final List<RenderObject> nodesToProcess =
        _nodesNeedingSemantics
            .where((RenderObject object) => !object._needsLayout && object.owner == this)
            .toList()
          ..sort((RenderObject a, RenderObject b) => a.depth - b.depth);
    _nodesNeedingSemantics.clear();
    if (!kReleaseMode) {
      FlutterTimeline.startSync('Semantics.updateChildren');
    }
    for (final RenderObject node in nodesToProcess) {
      if (node._semantics.parentDataDirty) {
        // This node is either blocked by a sibling
        // (via SemanticsConfiguration.isBlockingSemanticsOfPreviouslyPaintedNodes)
        // or is hidden by parent through visitChildrenForSemantics. Otherwise,
        // the parent node would have updated this node's parent data and it
        // the not be dirty.
        //
        // Updating the parent data now may create a gap of render object with
        // dirty parent data when this branch later rejoin the rendering tree.
        continue;
      }
      node._semantics.updateChildren();
    }
    if (!kReleaseMode) {
      FlutterTimeline.finishSync();
    }

    assert(() {
      assert(nodesToProcess.isEmpty || rootNode != null);
      if (rootNode != null) {
        _RenderObjectSemantics.debugCheckForParentData(rootNode!);
      }
      return true;
    }());

    if (!kReleaseMode) {
      FlutterTimeline.startSync('Semantics.ensureGeometry');
    }
    for (final RenderObject node in nodesToProcess) {
      if (node._semantics.parentDataDirty) {
        // same as above.
        continue;
      }
      node._semantics.ensureGeometry();
    }
    if (!kReleaseMode) {
      FlutterTimeline.finishSync();
    }

    if (!kReleaseMode) {
      FlutterTimeline.startSync('Semantics.ensureSemanticsNode');
    }
    for (final RenderObject node in nodesToProcess.reversed) {
      if (node._semantics.parentDataDirty) {
        // same as above.
        continue;
      }
      node._semantics.ensureSemanticsNode();
    }
    if (!kReleaseMode) {
      FlutterTimeline.finishSync();
    }

    _semanticsOwner!.sendSemanticsUpdate();
    for (final PipelineOwner child in _children) {
      child.flushSemantics();
    }
    assert(
      _nodesNeedingSemantics.isEmpty,
      'Child PipelineOwners must not dirty nodes in their parent.',
    );
  } finally {
    assert(() {
      _debugDoingSemantics = false;
      return true;
    }());
    if (!kReleaseMode) {
      FlutterTimeline.finishSync();
    }
  }
}