Tree#
파일 탐색기나 조직도처럼 계층적 데이터를 트리 구조로 표시하는 컴포넌트입니다.
Live Preview#
default
Web
Flutter
Loading Flutter...
class TreeDefaultExample extends StatelessComponent {
const TreeDefaultExample({super.key});
@override
Component build(BuildContext context) {
return const CoTree(
nodes: [
CoreTreeNode(
label: 'Documents',
expanded: true,
children: [
CoreTreeNode(label: 'Resume.pdf'),
CoreTreeNode(label: 'Cover Letter.pdf'),
],
),
CoreTreeNode(
label: 'Images',
children: [
CoreTreeNode(label: 'photo.png'),
CoreTreeNode(label: 'banner.jpg'),
],
),
CoreTreeNode(label: 'README.md'),
],
);
}
}
class TreeDefaultExample extends StatelessWidget {
const TreeDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return const CoTree(
nodes: [
CoreTreeNode(
label: 'Documents',
expanded: true,
children: [
CoreTreeNode(label: 'Resume.pdf'),
CoreTreeNode(label: 'Cover Letter.pdf'),
],
),
CoreTreeNode(
label: 'Images',
children: [
CoreTreeNode(label: 'photo.png'),
CoreTreeNode(label: 'banner.jpg'),
],
),
CoreTreeNode(label: 'README.md'),
],
);
}
}
selected
Web
Flutter
Loading Flutter...
class TreeSelectedExample extends StatelessComponent {
const TreeSelectedExample({super.key});
@override
Component build(BuildContext context) {
return const CoTree(
nodes: [
CoreTreeNode(
label: 'Documents',
expanded: true,
children: [
CoreTreeNode(label: 'Resume.pdf', selected: true),
CoreTreeNode(label: 'Cover Letter.pdf'),
],
),
CoreTreeNode(
label: 'Images',
children: [
CoreTreeNode(label: 'photo.png'),
],
),
],
);
}
}
class TreeSelectedExample extends StatelessWidget {
const TreeSelectedExample({super.key});
@override
Widget build(BuildContext context) {
return const CoTree(
nodes: [
CoreTreeNode(
label: 'Documents',
expanded: true,
children: [
CoreTreeNode(label: 'Resume.pdf', selected: true),
CoreTreeNode(label: 'Cover Letter.pdf'),
],
),
CoreTreeNode(
label: 'Images',
children: [
CoreTreeNode(label: 'photo.png'),
],
),
],
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 파일 탐색기처럼 폴더/파일 계층 구조를 표시할 때
- 조직도, 카테고리 분류처럼 부모-자식 관계의 데이터를 탐색할 때
대신 다른 컴포넌트를 사용하세요:
Accordion: 고정된 수의 섹션을 접기/펼치기로 표시할 때Menu: 계층 없이 평면적인 메뉴 목록에Collapsible: 단일 섹션의 접기/펼치기에
기본 사용법 (Basic Usage)#
CoTree(
nodes: [
CoreTreeNode(
label: 'Documents',
expanded: true,
children: [
CoreTreeNode(label: 'Resume.pdf'),
CoreTreeNode(label: 'Cover Letter.pdf'),
],
),
CoreTreeNode(
label: 'Images',
children: [
CoreTreeNode(label: 'photo.png'),
],
),
CoreTreeNode(label: 'README.md'),
],
onNodeSelect: (node) => print('selected: ${node.label}'),
onNodeToggle: (node) => print('toggled: ${node.label}'),
)
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
nodes |
List<CoreTreeNode> |
필수 | 트리 노드 목록 |
onNodeSelect |
ValueChanged<CoreTreeNode>? |
null |
자식이 없는 노드(leaf) 클릭 시 호출 |
onNodeToggle |
ValueChanged<CoreTreeNode>? |
null |
자식이 있는 노드가 펼침/접힘될 때 호출 |
treeStyle |
CoreTreeStyle<Color>? / CoreTreeStyle<CoreColor>? |
null |
인스턴스 스타일 (Style 시스템 참조) |
스타일 시스템 (Style System)#
Tree 의 모든 chrome / dimensional / nested-slot 오버라이드는 CoreTreeStyle<Clr> 단일 슬롯으로 흐릅니다 (Epic #1302 원칙 6/7/8). 시맨틱 (onNodeSelect,
onNodeToggle, nodes) 은 위젯 파라미터로 직접 전달합니다.
시맨틱 vs 스타일#
-
시맨틱 / behaviour: 위젯 파라미터로 직접 (
nodes,onNodeSelect,onNodeToggle) -
chrome / dimensional / 슬롯 스타일:
CoreTreeStyle한 곳으로 (nodeBackgroundColor/nodeSelectedColor/nodeHoverColor/nodePaddingH/nodePaddingV/indent/indentLineColor/indentLineThickness/nodeTextStyle/chevronIconStyle)
Resolve chain#
design system default for tree
→ CoreTreeTheme.style // 프로젝트 공통
→ parent component slot override
→ widget.treeStyle // 인스턴스별
각 nested 슬롯 스타일 (nodeTextStyle / chevronIconStyle) 은 자기 컴포넌트 (CoreTextStyle
/ CoreIconStyle) 자체 resolve chain 으로 다시 한 번 머지됩니다.
슬롯 매핑 (CoreTreeStyle 10 필드)#
| 필드 | 타입 (Flutter) | 적용 영역 |
|---|---|---|
nodeBackgroundColor | Color? | 노드 행 기본 배경 |
nodeSelectedColor | Color? | 노드 행 선택 배경 |
nodeHoverColor | Color? | 노드 행 호버 배경 |
nodePaddingH | double? | 노드 좌우 패딩 (px) |
nodePaddingV | double? | 노드 위/아래 패딩 (px) |
indent | double? | depth 별 들여쓰기 (px) |
indentLineColor | Color? | 들여쓰기 가이드 라인 색 |
indentLineThickness | double? | 들여쓰기 가이드 라인 두께 (px) |
nodeTextStyle |
CoreTextStyle<Color>? |
노드 라벨 텍스트 스타일 |
chevronIconStyle |
CoreIconStyle<Color>? |
펼침/접힘 chevron 아이콘 스타일 (CoIcon 슬롯으로 위임) |
Migration#
기존에는 인스턴스별 노드 색상/패딩/들여쓰기 / 텍스트 스타일을 노출하지 않았습니다. 새 코드는 treeStyle 슬롯을 사용하세요:
| 기존 (불가능) | 새 위치 |
|---|---|
| 인스턴스별 노드 배경/선택 색 | treeStyle.nodeBackgroundColor / nodeSelectedColor |
| 인스턴스별 노드 패딩 | treeStyle.nodePaddingH / nodePaddingV |
| 인스턴스별 들여쓰기 | treeStyle.indent (기존 CoreTreeTheme.indentSize 호환 유지) |
| 인스턴스별 라벨 텍스트 스타일 | treeStyle.nodeTextStyle |
| chevron 아이콘 색/크기 | treeStyle.chevronIconStyle (CoreIconStyle) |
사용 예 (Flutter)#
CoTree(
nodes: [
CoreTreeNode(label: 'Documents', expanded: true, children: [
CoreTreeNode(label: 'Resume.pdf'),
]),
],
treeStyle: CoreTreeStyle<Color>(
nodeSelectedColor: Colors.blue.shade50,
indent: 24,
chevronIconStyle: CoreIconStyle(size: 14, color: Colors.blueGrey),
),
)
사용 예 (Web)#
CoTree(
nodes: nodes,
treeStyle: CoreTreeStyle<CoreColor>(
indent: 24,
nodePaddingH: 12,
nodePaddingV: 6,
),
)
CoreTreeNode#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
label | String | 필수 | 노드 라벨 |
children |
List<CoreTreeNode> |
const [] |
자식 노드 |
expanded | bool | false | 초기 펼침 여부 |
id |
String? |
null |
안정적 식별자 (콜백에서 id로 특정 노드 지칭할 때) |
selected |
bool |
false |
선택 상태 시각 강조 (primary tint + text) |
동작 스펙 (Behavior)#
인터랙션#
- 자식이 있는 노드 클릭: 펼침/접힘 토글.
onNodeToggle호출 - 자식이 없는 노드(leaf) 클릭:
onNodeSelect가 제공된 경우 호출 - 아이콘: 자식 있음 →
folder+ chevron, 없음 →file
애니메이션#
- 자식이 있는 노드의 chevron 회전:
AnimationConstants.short(150ms) - Flutter
AnimatedRotation, Webtransition-transform동일 duration
접근성 (Accessibility)#
터치 타겟#
- 각 노드 행 최소 높이: 충분한 padding 확보
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | CoTree | CoTree |
| 렌더링 | Column + 재귀 Padding |
<div> + 재귀 padding-left |
| 아이콘 | CoIcon(CoLucideIcons.folder/file) |
CoIcon(CoLucideIcons.folder/file) |
| 회전 애니메이션 | AnimatedRotation | transition-transform |
| 테마 | CoreTreeTheme | CoreTreeTheme |
관련 컴포넌트 (Related Components)#
- Accordion: 고정된 수의 섹션을 그룹으로 접기/펼치기
- Menu: 계층 없는 평면 메뉴 목록
- Collapsible: 단일 섹션의 접기/펼치기