visitChildrenForSemantics method

  1. @override
void visitChildrenForSemantics(
  1. RenderObjectVisitor visitor
)
override

Filters out children whose nodes have been removed from the controller (or are mid-exit) so screen readers don't announce/focus them while the render boxes wait for their post-frame eviction.

Walks in visual order (sticky headers first, then in-flow visible nodes top-to-bottom) so screen readers announce rows in the same order the user sees them rather than raw insertion order.

Implementation

@override
void visitChildrenForSemantics(RenderObjectVisitor visitor) {
  // Sticky headers paint on top, shallowest first (visual top).
  for (final sticky in _sticky.headers) {
    final child = getChildForNode(sticky.nodeId);
    if (child == null) continue;
    if (controller.getNodeData(sticky.nodeId) == null) continue;
    if (controller.isExiting(sticky.nodeId)) continue;
    visitor(child);
  }
  // Then in-flow visible nodes, skipping any already emitted as sticky.
  // Iterate the MOUNTED set (bounded by the cache region plus retained
  // rows) rather than the full visible order, which would pay an
  // O(N_visible) sweep per semantics pass to visit O(mounted) children.
  // Sort by visible index so screen readers still get top-to-bottom
  // visual order.
  debugLastSemanticsIterationCount = 0;
  final inFlow = <(int, RenderBox)>[];
  for (final entry in _children.entries) {
    debugLastSemanticsIterationCount++;
    final nodeId = entry.key;
    final nid = _controller.nidOf(nodeId);
    if (nid < 0) continue;
    if (_sticky.isSticky(nid)) continue;
    if (controller.getNodeData(nodeId) == null) continue;
    if (controller.isExiting(nodeId)) continue;
    final idx = _controller.visibleIndexOfNid(nid);
    if (idx < 0) continue;
    inFlow.add((idx, entry.value));
  }
  inFlow.sort((a, b) => a.$1.compareTo(b.$1));
  for (final e in inFlow) {
    visitor(e.$2);
  }
}