Tree#
파일 탐색기나 조직도처럼 계층적 데이터를 트리 구조로 표시하는 컴포넌트입니다.
Live Preview#
default
Web
Flutter
Loading Flutter...
class TreeDefaultExample extends StatelessComponent {
const TreeDefaultExample({super.key});
@override
Component build(BuildContext context) {
return const Tree(
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 Tree(
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 Tree(
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 Tree(
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)#
Tree(
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/nodePadding/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? | 노드 행 호버 배경 |
nodePadding | CoreEdgeInsets? | 노드 행 패딩 |
indent | double? | depth 별 들여쓰기 (px) |
indentLineColor | Color? | 들여쓰기 가이드 라인 색 |
indentLineThickness | double? | 들여쓰기 가이드 라인 두께 (px) |
nodeTextStyle |
CoreTextStyle<Color>? |
노드 라벨 텍스트 스타일 |
chevronIconStyle |
CoreIconStyle<Color>? |
펼침/접힘 chevron 아이콘 스타일 (Icon 슬롯으로 위임) |
동작 스펙 (Behavior)#
인터랙션#
- 자식이 있는 노드 클릭: 펼침/접힘 토글.
onNodeToggle호출 - 자식이 없는 노드(leaf) 클릭:
onNodeSelect가 제공된 경우 호출 - 아이콘: 자식 있음 →
folder+ chevron, 없음 →file
애니메이션#
- 자식이 있는 노드의 chevron 회전:
AnimationConstants.short(150ms) - Flutter
AnimatedRotation, Webtransition-transform동일 duration
접근성 (Accessibility)#
터치 타겟#
- 각 노드 행 최소 높이: 충분한 padding 확보
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | Tree | Tree |
| 렌더링 | Column + 재귀 Padding |
<div> + 재귀 padding-left |
| 아이콘 | Icon(LucideIcons.folder/file) |
Icon(LucideIcons.folder/file) |
| 회전 애니메이션 | AnimatedRotation | transition-transform |
| 테마 | CoreTreeTheme | CoreTreeTheme |
관련 컴포넌트 (Related Components)#
- Accordion: 고정된 수의 섹션을 그룹으로 접기/펼치기
- Menu: 계층 없는 평면 메뉴 목록
- Collapsible: 단일 섹션의 접기/펼치기