flushSemantics method
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 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
// would 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();
}
final RenderObject? rootNode = this.rootNode;
assert(() {
assert(nodesToProcess.isEmpty || rootNode != null);
if (rootNode != null) {
_RenderObjectSemantics.debugCheckForParentData(rootNode);
}
return true;
}());
if (!kReleaseMode) {
FlutterTimeline.startSync('Semantics.ensureGeometry');
}
for (final 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();
}
}
}