FAB#
화면의 주요 액션을 강조하는 플로팅 액션 버튼(Floating Action Button)입니다. Fab은 단독 버튼으로도, 탭하면 액션 메뉴가 열리는 트리거로도 사용할 수 있습니다 — 열린 메뉴는
Popup 패널 안에 Button(variant: .menu) 행이 하나씩 놓인 형태입니다. Flutter와 Web이 동일한 Fab
API를 공유합니다.
Live Preview#
class FabDefaultExample extends StatelessComponent {
const FabDefaultExample({super.key});
@override
Component build(BuildContext context) {
return Fab(
trigger: const Icon(LucideIcons.plus),
onPressed: () {},
expandDirection: CoreFabExpandDirection.down,
actions: [
FabAction(
icon: const Icon(LucideIcons.pencil),
label: Text('Edit'),
),
FabAction(
icon: const Icon(LucideIcons.share2),
label: Text('Share'),
),
],
);
}
}
class FabDefaultExample extends StatelessWidget {
const FabDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return Fab(
trigger: const Icon(LucideIcons.plus),
onPressed: () {},
expandDirection: CoreFabExpandDirection.down,
actions: [
FabAction(
icon: const Icon(LucideIcons.pencil),
label: const Text('Edit'),
),
FabAction(
icon: const Icon(LucideIcons.share2),
label: const Text('Share'),
),
],
);
}
}
class FabChainExample extends StatelessComponent {
const FabChainExample({super.key});
@override
Component build(BuildContext context) {
return Fab(
trigger: const Icon(LucideIcons.plus),
onPressed: () {},
expandDirection: CoreFabExpandDirection.down,
actions: [
FabAction(
icon: const Icon(LucideIcons.pencil),
label: Text('Edit'),
),
FabAction(
icon: const Icon(LucideIcons.share2),
label: Text('Share'),
),
],
).withStyle(
const CoreFabStyle(
diameter: CoreSize.size72,
iconStyle: CoreIconStyle(
size: CoreIconSize.size32,
color: CoreColor.token(CoreColors.onPrimary),
),
popupStyle: CorePopupStyle(
backgroundColor: CoreColor.token(CoreColors.primaryContainer),
borderRadius: CoreBorderRadius.all(CoreRadius.radius16),
),
spacing: CoreSpace.space24,
),
);
}
}
class FabChainExample extends StatelessWidget {
const FabChainExample({super.key});
@override
Widget build(BuildContext context) {
return Fab(
trigger: const Icon(LucideIcons.plus),
onPressed: () {},
expandDirection: CoreFabExpandDirection.down,
actions: [
FabAction(
icon: const Icon(LucideIcons.pencil),
label: const Text('Edit'),
),
FabAction(
icon: const Icon(LucideIcons.share2),
label: const Text('Share'),
),
],
).withStyle(
const CoreFabStyle(
diameter: CoreSize.size72,
iconStyle: CoreIconStyle(
size: CoreIconSize.size32,
color: CoreColor.token(CoreColors.onPrimary),
),
popupStyle: CorePopupStyle(
backgroundColor: CoreColor.token(CoreColors.primaryContainer),
borderRadius: CoreBorderRadius.all(CoreRadius.radius16),
),
spacing: CoreSpace.space24,
),
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 화면 내에서 가장 중요한 단일 액션을 명확히 강조하고 싶을 때 (새 항목 만들기, 작성, 공유)
- 스크롤해도 항상 접근 가능해야 하는 주요 CTA(Call to Action)가 필요할 때
- 하나의 트리거에서 여러 관련 액션을 메뉴로 열어 보여주고 싶을 때
대신 다른 컴포넌트를 사용하세요:
Button: 여러 동등한 액션을 나란히 제공할 때, 또는 인라인 액션이 필요할 때Tooltip: 아이콘 전용 FAB에 레이블 설명을 추가할 때 함께 사용
기본 사용법 (Basic Usage)#
// 기본 FAB
Fab(
trigger: const Icon(LucideIcons.plus),
onPressed: handleCreate,
)
// 소형 FAB
Fab(
trigger: const Icon(LucideIcons.plus),
size: CoreFabSize.mini,
onPressed: handleCreate,
)
// 메뉴 FAB (탭하면 액션 메뉴가 열림)
Fab(
trigger: const Icon(LucideIcons.plus),
actions: [
FabAction(
icon: const Icon(LucideIcons.pencil),
label: const Text('Edit'),
onPressed: handleEdit,
),
FabAction(
icon: const Icon(LucideIcons.share2),
label: const Text('Share'),
onPressed: handleShare,
),
],
)
// 기본 FAB
Fab(
trigger: const Icon(LucideIcons.plus),
onPressed: handleCreate,
)
// 소형 FAB
Fab(
trigger: const Icon(LucideIcons.plus),
size: CoreFabSize.mini,
onPressed: handleCreate,
)
// 메뉴 FAB
Fab(
trigger: const Icon(LucideIcons.plus),
actions: [
FabAction(
icon: const Icon(LucideIcons.pencil),
label: const Text('Edit'),
onPressed: handleEdit,
),
FabAction(
icon: const Icon(LucideIcons.share2),
label: const Text('Share'),
onPressed: handleShare,
),
],
)
빠른 오버라이드 (Chain)#
이미 만든 Fab 인스턴스에 스타일을 빠르게 덧붙이고 싶다면 withStyle 체인을 쓸 수 있습니다. 생성자의 style 슬롯 인자와 동일하게 동작하지만, 이미 구성된 위젯 위에서 바로 이어 쓸 수 있습니다.
class FabChainExample extends StatelessWidget {
const FabChainExample({super.key});
@override
Widget build(BuildContext context) {
return Fab(
trigger: const Icon(LucideIcons.plus),
onPressed: () {},
expandDirection: CoreFabExpandDirection.down,
actions: [
FabAction(
icon: const Icon(LucideIcons.pencil),
label: const Text('Edit'),
),
FabAction(
icon: const Icon(LucideIcons.share2),
label: const Text('Share'),
),
],
).withStyle(
const CoreFabStyle(
diameter: CoreSize.size72,
iconStyle: CoreIconStyle(
size: CoreIconSize.size32,
color: CoreColor.token(CoreColors.onPrimary),
),
popupStyle: CorePopupStyle(
backgroundColor: CoreColor.token(CoreColors.primaryContainer),
borderRadius: CoreBorderRadius.all(CoreRadius.radius16),
),
spacing: CoreSpace.space24,
),
);
}
}
class FabChainExample extends StatelessComponent {
const FabChainExample({super.key});
@override
Component build(BuildContext context) {
return Fab(
trigger: const Icon(LucideIcons.plus),
onPressed: () {},
expandDirection: CoreFabExpandDirection.down,
actions: [
FabAction(
icon: const Icon(LucideIcons.pencil),
label: Text('Edit'),
),
FabAction(
icon: const Icon(LucideIcons.share2),
label: Text('Share'),
),
],
).withStyle(
const CoreFabStyle(
diameter: CoreSize.size72,
iconStyle: CoreIconStyle(
size: CoreIconSize.size32,
color: CoreColor.token(CoreColors.onPrimary),
),
popupStyle: CorePopupStyle(
backgroundColor: CoreColor.token(CoreColors.primaryContainer),
borderRadius: CoreBorderRadius.all(CoreRadius.radius16),
),
spacing: CoreSpace.space24,
),
);
}
}
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
trigger |
Widget / Component |
필수 | 메인 FAB 내부에 표시할 콘텐츠 (보통 아이콘) |
onPressed |
VoidCallback? |
null |
메인 FAB 클릭 핸들러. actions가 있으면 메뉴 토글로 동작 |
actions |
List<FabAction>? |
null |
메뉴 행이 되는 액션 목록 |
open |
bool |
false |
메뉴 열림 상태 (controlled 모드) |
onOpenChanged |
void Function(bool)? |
null |
메뉴 열림 상태 변경 콜백 |
size |
CoreFabSize |
CoreFabSize.regular |
FAB 크기 (mini / regular / large) |
expandDirection |
CoreFabExpandDirection |
CoreFabExpandDirection.up |
메뉴가 열리는 방향 (
up
/
down
/
left
/
right
/
fan
—
fan
은
up
과 같다)
|
closeIcon |
Widget? / Component? |
null |
메뉴 열림 시 표시할 아이콘 (기본값: X 아이콘) |
tooltip |
String? |
null |
메인 FAB 툴팁 텍스트 |
fabStyle |
CoreFabStyle? |
null |
지름 / 간격 / 애니메이션 + nested
buttonStyle
·
actionButtonStyle
·
iconStyle
·
popupStyle
·
popoverStyle
등 모든 chrome 단일 진입점
|
FabAction#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
icon |
Widget / Component |
필수 | 메뉴 행의 leading 아이콘 |
label |
Widget? / Component? |
null |
메뉴 행의 라벨 |
onPressed |
VoidCallback? |
null |
액션 클릭 핸들러 |
tooltip |
String? |
null |
액션 툴팁 텍스트 |
스타일 시스템 (Style System)#
CoreFabStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
buttonStyle |
CoreButtonStyle? |
Main FAB circle chrome override — raw-forwarded to the composed
Button
. Carries background / foreground / shadow / radius / focus ring.
null
(or any unset field) defers to [defaultButtonStyle].
|
actionButtonStyle |
CoreButtonStyle? |
Action-row chrome override — raw-forwarded to each composed
Button(variant:.menu)
row.
null
defers to [defaultActionButtonStyle].
|
actionLabelTextStyle |
CoreTextStyle? |
Action-row label text style override. Text colour is carried via [CoreTextStyle.color] inside this slot. Defaults to [defaultActionLabelTextStyle]. |
actionLabelBackgroundColor |
CoreColor? |
Retired — see [defaultActionLabelBackgroundColor]. Read by nothing. |
actionLabelBoxShadow |
List<CoreShadowLayer>? |
Retired — see [defaultActionLabelBoxShadow]. Read by nothing. |
actionLabelPadding |
CoreEdgeInsets? |
Retired — see [defaultActionLabelPadding]. Read by nothing. |
actionLabelBorderRadius |
CoreBorderRadius? |
Retired — see [defaultActionLabelBorderRadius]. Read by nothing. |
diameter |
double? |
Main FAB circular diameter override (logical px).
null
defers to [defaultsBySize] for the active size. Forwarded to the composed
Button
's
width
/
height
.
|
iconStyle |
CoreIconStyle? |
Main FAB icon style override — sizes the icon glyph the FAB passes as the composed
Button
's child.
null
defers to [defaultsBySize] for the active size.
|
actionDiameter |
double? |
Retired — see [defaultActionDiameter]. Read by nothing. |
actionIconStyle |
CoreIconStyle? |
Action-row leading icon style override — baked into the row
Button
's
leadingIconStyle
.
null
defers to [defaultActionIconStyle].
|
spacing |
double? |
Gap between the main FAB and the menu panel (logical px) override. Pure placement-offset scalar — never consumed as a flex gap by
Gap
/ CSS
gap
.
|
actionGapStyle |
CoreGapStyle? |
Retired — see [defaultActionGapStyle]. Read by nothing. |
duration |
Duration? |
Menu open / close animation duration override — the panel's own open / close motion, unless [popupStyle] pins one. |
popoverStyle |
CorePopoverStyle? |
Overlay host chrome override. Nested [CorePopoverStyle] raw-forwarded to the composed
Popover
that hosts the menu (child-component-composition).
null
(or any unset field) defers to [defaultPopoverStyle] — chrome-less, because the visible box is the
Popup
in [popupStyle]. Override e.g. to tune the placement offset.
|
popupStyle |
CorePopupStyle? |
Menu panel chrome override. Nested [CorePopupStyle] raw-forwarded to the composed
Popup
that holds the action rows.
null
(or any unset field) defers to [defaultPopupStyle] — the same panel a
Menu
draws.
|
크기 (Sizes)#
| 크기 | 값 | 지름 |
|---|---|---|
| Mini | CoreFabSize.mini | 40px |
| Regular | CoreFabSize.regular | 56px |
| Large | CoreFabSize.large | 72px |
동작 스펙 (Behavior)#
인터랙션#
-
클릭/탭:
actions가 없으면onPressed콜백 실행.actions가 있으면 speed-dial을 펼치거나 접음 - speed-dial 액션 클릭: 해당 액션의
onPressed실행 후 speed-dial 자동 닫힘 - 호버: 커서가 포인터로 전환
- 포커스: 포커스 링 표시로 키보드 탐색 가능 상태 표시
Controlled / Uncontrolled#
onOpenChanged를 제공하지 않으면Fab이 내부 상태로 speed-dial 열림/닫힘을 관리합니다 (uncontrolled)onOpenChanged를 제공하면open값으로 외부에서 상태를 제어합니다 (controlled)
애니메이션#
- speed-dial 펼침/접힘: 200ms ease-in-out, 액션별 stagger 적용
- 펼침 방향(
expandDirection)에 따라 액션이 동일한 계산식으로 배치됨
사용 가이드라인 (Usage Guidelines)#
✅ Do#
한 화면에 하나의 FAB만 사용
Fab(
trigger: const Icon(LucideIcons.plus),
tooltip: '새 노트 작성',
onPressed: handleCreateNote,
)
FAB은 화면에서 가장 중요한 단일 액션을 나타내야 합니다. 여러 개를 사용하면 사용자가 어느 것이 주요 액션인지 혼란스러워집니다.
❌ Don't#
파괴적인 액션에 FAB 사용 금지
// ❌ 삭제(파괴적) 액션에 FAB 사용
Fab(
trigger: const Icon(LucideIcons.trash2),
onPressed: handleDeleteAll,
)
FAB은 눈에 잘 띄기 때문에 실수로 클릭하기 쉽습니다. 삭제 같은 파괴적인 액션에는 확인 다이얼로그가 있는 일반 버튼을 사용하세요.
✅ Do#
speed-dial로 관련 액션을 묶어 제공
Fab(
trigger: const Icon(LucideIcons.plus),
actions: [
FabAction(
icon: const Icon(LucideIcons.pencil),
label: const Text('Edit'),
onPressed: handleEdit,
),
FabAction(
icon: const Icon(LucideIcons.share2),
label: const Text('Share'),
onPressed: handleShare,
),
],
)
하나의 트리거에서 관련된 보조 액션을 펼쳐 보여주면 화면을 깔끔하게 유지할 수 있습니다.
접근성 (Accessibility)#
키보드 인터랙션#
| 키 | 동작 |
|---|---|
Enter / Space | FAB 액션 실행 또는 speed-dial 토글 |
Tab | 다음 포커스 가능 요소로 이동 |
스크린 리더#
- Flutter:
Tooltip위젯으로 버튼 레이블 전달.tooltip속성 설정 권장 -
Web:
role="button",aria-label,aria-expanded가 자동 적용
터치 타겟#
- Mini: 40px (중요도 낮은 보조 액션에만 사용)
- Regular: 56px (기본, 권장)
- Large: 72px (특별히 강조가 필요한 경우)
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | Fab | Fab |
| 클릭 핸들러 | onPressed | onPressed |
| elevation | CoreShadow 토큰 → BoxShadow |
CoreShadow 토큰 → CSS box-shadow |
| speed-dial 토글 | controlled open 상태 + 애니메이션 |
controlled open 상태 + CSS transition |