visibleRows method
The visible rows in render order. While searching, only matches and their ancestors are shown, and ancestor folders are force-expanded so the hits are revealed.
Implementation
List<TreeRow<T>> visibleRows() {
final q = _query.trim().toLowerCase();
final filter = q.isNotEmpty;
final matched = <TreeNodeId>{};
final onPath = <TreeNodeId>{}; // ancestors of a match
if (filter) {
void rec(List<TreeNode<T>> nodes, List<TreeNodeId> path) {
for (final n in nodes) {
if (n.label.toLowerCase().contains(q)) {
matched.add(n.id);
onPath.addAll(path);
}
if (n.children.isNotEmpty) rec(n.children, [...path, n.id]);
}
}
rec(_roots, const []);
}
bool visible(TreeNode<T> n) => !filter || matched.contains(n.id) || onPath.contains(n.id);
bool expandedFor(TreeNode<T> n) => filter ? onPath.contains(n.id) : _expanded.contains(n.id);
final rows = <TreeRow<T>>[];
void emit(List<TreeNode<T>> nodes, int depth, List<bool> ancestorHasNext) {
final shown = filter ? nodes.where(visible).toList() : nodes;
for (var i = 0; i < shown.length; i++) {
final n = shown[i];
final isLast = i == shown.length - 1;
final expanded = expandedFor(n);
rows.add(TreeRow<T>(
node: n,
depth: depth,
expanded: expanded,
hasChildren: n.hasChildren,
isLast: isLast,
ancestorHasNext: List<bool>.of(ancestorHasNext),
));
if (n.hasChildren && expanded) {
emit(n.children, depth + 1, [...ancestorHasNext, !isLast]);
}
}
}
emit(_roots, 0, const []);
return rows;
}