buildTreeTableRows<T> function

List<TreeTableRow<T>> buildTreeTableRows<T>({
  1. required List<TreeTableNode<T>> roots,
  2. required List<DataTableColumn> columns,
  3. Set<Object> expandedKeys = const <Object>{},
  4. String? treeColumnId,
  5. TreeTableCellBuilder<T>? cellBuilder,
  6. TreeTableFilterDescriptor? filter,
  7. TreeTableSearchIndex<T>? searchIndex,
})

Builds visible rows for a TreeTable.

Without a filter, only descendants of expanded nodes are returned. With a filter, matching rows and their ancestors are returned regardless of current expansion so collapsed matches remain discoverable.

Implementation

List<TreeTableRow<T>> buildTreeTableRows<T>({
  required List<TreeTableNode<T>> roots,
  required List<DataTableColumn> columns,
  Set<Object> expandedKeys = const <Object>{},
  String? treeColumnId,
  TreeTableCellBuilder<T>? cellBuilder,
  TreeTableFilterDescriptor? filter,
  TreeTableSearchIndex<T>? searchIndex,
}) {
  final effectiveTreeColumnId = treeColumnId ?? _firstColumnId(columns);
  final effectiveFilter = filter ?? const TreeTableFilterDescriptor();
  if (effectiveFilter.isEmpty) {
    final rows = <TreeTableRow<T>>[];
    void visit(
      TreeTableNode<T> node,
      int depth,
      String path,
      Object? parentKey,
    ) {
      rows.add(
        TreeTableRow<T>(
          node: node,
          depth: depth,
          path: path,
          parentKey: parentKey,
        ),
      );
      if (!expandedKeys.contains(node.key)) return;
      for (var i = 0; i < node.children.length; i++) {
        visit(node.children[i], depth + 1, '$path.$i', node.key);
      }
    }

    for (var i = 0; i < roots.length; i++) {
      visit(roots[i], 0, '$i', null);
    }
    return List<TreeTableRow<T>>.unmodifiable(rows);
  }

  if (searchIndex != null) {
    return searchIndex.filter(effectiveFilter);
  }

  final query = effectiveFilter.caseSensitive
      ? _sanitizeTreeTableText(effectiveFilter.query.trim())
      : _sanitizeTreeTableText(effectiveFilter.query.trim()).toLowerCase();
  final rows = <TreeTableRow<T>>[];

  bool visitFiltered(
    TreeTableNode<T> node,
    int depth,
    String path,
    Object? parentKey,
  ) {
    final row = TreeTableRow<T>(
      node: node,
      depth: depth,
      path: path,
      parentKey: parentKey,
    );
    final childRows = <TreeTableRow<T>>[];
    var childMatched = false;
    for (var i = 0; i < node.children.length; i++) {
      final before = rows.length;
      if (visitFiltered(node.children[i], depth + 1, '$path.$i', node.key)) {
        childMatched = true;
        childRows.addAll(rows.sublist(before));
      }
      rows.removeRange(before, rows.length);
    }
    final matched = _rowMatchesFilter(
      row,
      columns,
      effectiveTreeColumnId,
      cellBuilder,
      effectiveFilter,
      query,
    );
    if (!matched && !childMatched) return false;
    rows.add(row);
    rows.addAll(childRows);
    return true;
  }

  for (var i = 0; i < roots.length; i++) {
    visitFiltered(roots[i], 0, '$i', null);
  }
  return List<TreeTableRow<T>>.unmodifiable(rows);
}