PanelHeader
패널 머리 — 제목과 줄 끝의 작은 행동 하나
목차
PanelHeader 는 페이지 안의 패널 · 카드 하나의 머리입니다. 제목 한 줄과 줄 끝의 작은
행동 하나(보통 plain · sm 버튼에 뒤 화살표 chevron-right
14, "전체 보기 >" 꼴)를 놓고, 그 아래를 패널 본문과 띄웁니다. 폭이 좁아지면
행동이 제목 아래로 내려갑니다 — 머리는 넘치지 않습니다.
기본 사용법 (Basic Usage)#
PanelHeader(
title: const Text('Recent files'),
action: Button(
variant: .plain,
size: .sm,
onPressed: () {},
child: const Text('View all'),
trailing: const Icon(
LucideIcons.chevronRight,
iconStyle: CoreIconStyle(size: CoreIconSize.size14),
),
),
)
PanelHeader(
title: const Text('Recent files'),
action: Button(
variant: .plain,
size: .sm,
onPressed: () {},
child: const Text('View all'),
trailing: const Icon(
LucideIcons.chevronRight,
iconStyle: CoreIconStyle(size: CoreIconSize.size14),
),
),
)
제목에 넘긴 Text 는 머리가 정한 역할 · 색으로 그려집니다. 폭은 부모가 정합니다 — 머리는
받은 폭을 채웁니다.
Live Preview#
Recent files
코드 보기
class PanelHeaderDefaultExample extends StatelessComponent {
const PanelHeaderDefaultExample({super.key});
@override
Component build(BuildContext context) {
return PanelHeader(
title: const Text('Recent files'),
action: Button(
variant: .plain,
size: .sm,
onPressed: () {},
child: const Text('View all'),
trailing: const Icon(
LucideIcons.chevronRight,
iconStyle: CoreIconStyle(size: CoreIconSize.size14),
),
),
);
}
}
class PanelHeaderDefaultExample extends StatelessWidget {
const PanelHeaderDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return PanelHeader(
title: const Text('Recent files'),
action: Button(
variant: .plain,
size: .sm,
onPressed: () {},
child: const Text('View all'),
trailing: const Icon(
LucideIcons.chevronRight,
iconStyle: CoreIconStyle(size: CoreIconSize.size14),
),
),
);
}
}
구조 (Anatomy)#
제목과 행동 두 칸입니다. 제목이 행동이 남긴 폭을 모두 차지합니다.
| 부분 | 역할 | 슬롯 |
|---|---|---|
title |
패널 제목 — headingLevel 단계의 머리글 |
title |
action |
줄 끝의 행동 하나 — 보통 Button(variant: .plain, size: .sm) + 뒤 chevron-right |
action |
Figma: ❖ PanelHeader
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 카드 · 패널 · 섹션 하나의 제목 줄에 "모두 보기" · "추가" 같은 작은 행동을 함께 둘 때
- 페이지 제목 아래 단계(머리글 2단계)의 제목이 필요할 때
대신 다른 컴포넌트를 사용하세요:
- PageHeader: 페이지 맨 위의 머리 — 보조 설명 · 큰 제목 · 설명과 여러 행동
Props / Parameters#
W 는 Flutter의 Widget / Web의 Jaspr Component입니다.
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
title |
W |
필수 | 패널 제목. headingLevel 단계의 머리글로 읽힙니다 |
action |
W? |
null |
줄 끝의 행동 하나. 없으면 제목만 그립니다 |
headingLevel |
int |
CorePanelHeaderContract.defaultHeadingLevel(2) |
제목의 머리글 단계 1–6. 페이지 제목 아래의 패널은 보통 2 입니다 |
panelHeaderStyle |
CorePanelHeaderStyle? |
null |
모든 chrome 이 지나는 단일 슬롯 (아래 표 참고) |
CorePanelHeaderStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
titleTextStyle |
CoreTextStyle? |
Ambient text style of the title slot, merged on top of [defaultTitleTextStyle]. |
padding |
CoreEdgeInsets? |
Inset around the row (pre-scaling). null → [defaultPadding]. |
titleActionSpacing |
double? |
Space between the title and the action — across the row while they sit side by side, and down the column once the action has moved under the title (logical px, pre-scaling).
null
→ [defaultTitleActionSpacing].
|
minTitleWidth |
double? |
The narrowest the title may get beside the action (logical px, pre-scaling). Below it the action moves under the title.
null
→ [defaultMinTitleWidth].
|
스타일 시스템 (Style System)#
panelHeaderStyle#
- 시맨틱 / behaviour: 위젯 파라미터로 직접 (
headingLevel) -
chrome / dimensional:
CorePanelHeaderStyle한 곳으로 (제목 글자 스타일, 머리 여백, 제목과 행동 사이 간격, 제목의 최소 폭)
제목은 titleMedium(18 · Bold) · onSurface 입니다. 머리 여백은 아래쪽 8(패널 본문과의
간격), 제목과 행동 사이(titleActionSpacing)는 8 입니다. 둘은 간격 사다리 위의 값이라
밀도(CoreDensity)가 한 칸 옮깁니다. 제목의 최소 폭(minTitleWidth) 128 은 폭이라
움직이지 않습니다.
Resolve chain
CorePanelHeaderStyle.defaultX // 기본값
→ CorePanelHeaderTheme.style // 프로젝트 공통
→ widget.panelHeaderStyle // 인스턴스별
사용 예
CoreComponentTheme(
panelHeader: CorePanelHeaderTheme(
style: CorePanelHeaderStyle(
padding: CoreEdgeInsets.only(bottom: CoreSpace.space12),
),
),
)
빠른 오버라이드 (Chain)
이미 만든 PanelHeader 인스턴스에 스타일을 빠르게 덧붙이고 싶다면 withStyle
체인을 쓸 수 있습니다. 생성자의 style 슬롯 인자와 동일하게 동작하지만,
이미 구성된 위젯 위에서 바로 이어 쓸 수 있습니다.
class PanelHeaderChainExample extends StatelessWidget {
const PanelHeaderChainExample({super.key});
@override
Widget build(BuildContext context) {
return PanelHeader(
title: const Text('Recent files'),
action: Button(
variant: .plain,
size: .sm,
onPressed: () {},
child: const Text('View all'),
trailing: const Icon(
LucideIcons.chevronRight,
iconStyle: CoreIconStyle(size: CoreIconSize.size14),
),
),
).withStyle(
const CorePanelHeaderStyle(
titleTextStyle: CoreTextStyle.token(CoreTextStyles.titleSmall),
padding: CoreEdgeInsets.only(bottom: CoreSpace.space12),
),
);
}
}
class PanelHeaderChainExample extends StatelessComponent {
const PanelHeaderChainExample({super.key});
@override
Component build(BuildContext context) {
return PanelHeader(
title: const Text('Recent files'),
action: Button(
variant: .plain,
size: .sm,
onPressed: () {},
child: const Text('View all'),
trailing: const Icon(
LucideIcons.chevronRight,
iconStyle: CoreIconStyle(size: CoreIconSize.size14),
),
),
).withStyle(
const CorePanelHeaderStyle(
titleTextStyle: CoreTextStyle.token(CoreTextStyles.titleSmall),
padding: CoreEdgeInsets.only(bottom: CoreSpace.space12),
),
);
}
}
동작 스펙 (Behavior)#
배치#
-
나란히: 행동이 자기 폭을 차지하고, 제목이 남은 폭을 모두 차지합니다. 둘은 세로 가운데가
맞습니다 — 제목 한 줄과
sm버튼(32)이 한 줄로 읽힙니다 -
행동이 아래로: 제목이 받을 폭이
minTitleWidth(128)보다 좁아지면 행동이 제목 아래로 내려가 시작 쪽에 붙습니다. 제목과의 간격은 나란할 때와 같은titleActionSpacing입니다 - 방향: 오른쪽에서 왼쪽으로 쓰는 로케일에서 두 배치가 함께 뒤집힙니다
-
폭이 정해지지 않은 자리: 폭이 정해지지 않은 자리에서는 두 플랫폼 모두 제목을 내용 폭으로
잽니다 — 제목이
minTitleWidth보다 짧으면 행동이 아래로 내려갑니다. 머리는 폭이 정해진 패널에 놓습니다
인터랙션#
- 머리 자체는 입력을 받지 않습니다 — 행동의 동작은 행동이 가집니다
상태 전환#
- 상태가 없습니다
애니메이션#
- 없습니다
사용 가이드라인 (Usage Guidelines)#
Do#
행동은 작고 조용하게
PanelHeader(
title: const Text('Recent files'),
action: Button(
variant: .plain,
size: .sm,
onPressed: viewAll,
child: const Text('View all'),
trailing: const Icon(
LucideIcons.chevronRight,
iconStyle: CoreIconStyle(size: CoreIconSize.size14),
),
),
)
이유: 패널 머리의 행동은 그 패널에 대한 보조 행동입니다. plain · sm 이면 바탕이 없어 제목 줄의
높이를 키우지 않고, 페이지의 주 행동(PageHeader 의 primary)과 겨루지 않습니다.
Don't#
행동을 여러 개 이어 붙이지 않는다
// ❌ action 은 하나다 — Row 로 묶어 넣으면 좁은 폭에서 묶음째 내려가 줄을 바꾸지 못한다
PanelHeader(title: const Text('Files'), action: Row(children: [a, b, c]))
// ✅ 행동이 여럿이면 페이지 머리의 행동으로 올리거나, 메뉴 하나로 접는다
PanelHeader(title: const Text('Files'), action: moreMenuButton)
이유: 패널 머리는 행동 하나를 위한 자리입니다. 여러 행동의 줄 바꿈은 PageHeader 가
가집니다.
접근성 (Accessibility)#
역할#
-
Flutter: 제목을
Semantics(headingLevel:)로 감쌉니다 — 웹에서는aria-level, Android 에서는isHeading으로 나갑니다.header: true는 쓰지 않습니다 — 이 킷에서header:는 페이지 배너 랜드마크의 몫입니다 -
Web: 제목이
<h1>…<h6>요소(headingLevel, 기본<h2>)입니다. 브라우저 기본 여백은 지웁니다
키보드#
| 키 | Flutter | Web |
|---|---|---|
Tab | 행동으로 | 행동으로 |
머리 자체는 포커스를 받지 않습니다.
스크린 리더#
제목(머리글) → 행동 순서로 읽힙니다. 상태 통보(live region)는 없습니다.
포커스 관리#
- 진입 · 이탈 · 트랩: 없음
알려진 제약#
- 머리글 단계는 호출자가 고릅니다. 기본값 2 는 페이지 제목(1) 바로 아래의 패널을 가정합니다
전역 축(동작 줄이기 · 고대비 · 색 강제 모드 · 시맨틱 역할 · 최소 터치 타겟)은 접근성 페이지가 한 곳에서 다룹니다.
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | PanelHeader | PanelHeader |
| 배치 |
렌더 오브젝트(
HeaderWrapLayout
,
PageHeader
와 같은 것)가 행동의 폭을 재고, 제목에 남는 폭이
minTitleWidth
보다 좁으면 행동을 아래로 내립니다
|
flex-wrap
줄 — 제목의
flex-basis
가
minTitleWidth
, 하나의
gap
이 가로 · 세로 간격입니다
|
| 머리글 | Semantics(headingLevel:) |
<h1>…<h6> |
| 최소 폭의 단위 | 논리 px × 레이아웃 배율 | rem — 브라우저 글자 배율을 따릅니다 |
| 크기 질의 | IntrinsicWidth · IntrinsicHeight · Table 안에서도 답합니다 |
해당 없음 — 브라우저 배치 |
| Web 전용 HTML 표면 | — |
attributes
·
classes
·
css
·
eventHandlers
·
id
통과
|
위 차이 밖에는 없습니다 — 이름 · 순서 · 기본값을 두 위젯 생성자에서 대조했습니다.
관련 컴포넌트 (Related Components)#
- PageHeader: 페이지 맨 위의 머리 — 같은 배치 규칙
- Card: 머리를
header슬롯에 놓는 패널 - Button:
action에 놓는 행동
조합 예제#
Card(
header: PanelHeader(
title: const Text('Members'),
action: Button(
variant: .plain,
size: .sm,
onPressed: invite,
child: const Text('Invite'),
),
),
body: const Text('3 members'),
)