FAB | CoUI
본문으로 건너뛰기
LogoCoUI

FAB

화면의 주요 액션을 강조하는 플로팅 액션 버튼 컴포넌트

목차

화면의 주요 액션을 강조하는 플로팅 액션 버튼(Floating Action Button)입니다. Fab은 단독 버튼으로도, label을 주면 라벨이 붙은 Extended 알약으로도, 탭하면 액션 메뉴가 열리는 트리거로도 사용할 수 있습니다 — 열린 메뉴는 Popup 패널 안에 MenuItem 행이 하나씩 놓인 카드이고, 패널은 FAB 의 끝 가장자리(오른쪽 아래 FAB 이면 오른쪽 끝)에 맞춰 열립니다. Flutter와 Web이 동일한 Fab API를 공유합니다.

기본 사용법 (Basic Usage)#

// 기본 FAB
Fab(
  trigger: const Icon(LucideIcons.plus),
  onPressed: handleCreate,
)

// 소형 FAB
Fab(
  trigger: const Icon(LucideIcons.plus),
  size: CoreFabSize.mini,
  onPressed: handleCreate,
)

// Extended FAB (라벨이 붙은 알약 — 데스크톱 기본형)
Fab(
  trigger: const Icon(LucideIcons.plus),
  label: const Text('New note'),
  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,
)

// Extended FAB (라벨이 붙은 알약 — 데스크톱 기본형)
Fab(
  trigger: const Icon(LucideIcons.plus),
  label: Text('New note'),
  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,
      ),
    );
  }
}

Live Preview#

사용 시기 (When to Use)#

이 컴포넌트를 사용하세요:

  • 화면 내에서 가장 중요한 단일 액션을 명확히 강조하고 싶을 때 (새 항목 만들기, 작성, 공유)
  • 스크롤해도 항상 접근 가능해야 하는 주요 CTA(Call to Action)가 필요할 때
  • 하나의 트리거에서 여러 관련 액션을 메뉴로 열어 보여주고 싶을 때

대신 다른 컴포넌트를 사용하세요:

  • Button: 떠 있는 액션 대신 인라인 액션이 필요할 때
  • Tooltip: 대체 버튼이 아니라 아이콘 FAB의 짧은 도움말을 조합할 때

Props / Parameters#

W 는 Flutter의 Widget, Web의 Jaspr Component 입니다. W? 는 선택 슬롯입니다.

속성타입기본값설명
triggerW필수메인 FAB 내부에 표시할 콘텐츠 (보통 아이콘)
label W? null 값을 주면 Extended FAB(원이 아니라 알약)이 된다. trigger 는 leading 아이콘 자리로 옮겨간다. actions 와 동시에 지정할 수 없다
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 W? null 메뉴 열림 시 표시할 아이콘 (기본값: X 아이콘)
tooltip String? null 메인 FAB 툴팁 텍스트
fabStyle CoreFabStyle? null 지름 / 간격 / 애니메이션 + nested buttonStyle · actionItemStyle · iconStyle · popupStyle · popoverStyle 등 모든 chrome 단일 진입점

FabAction#

속성타입기본값설명
iconW필수메뉴 행의 leading 아이콘
labelW?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]. Three fields are owned elsewhere and restated after this slot, so a value here does not reach the circle: height / width belong to [diameter] (the circle needs them equal), and the glyph size in leadingIconStyle belongs to [iconStyle]. Every other leadingIconStyle sub-field (a colour, say) is merged on top of [iconStyle] and reaches the glyph.
extendedButtonStyle CoreButtonStyle? Extended (라벨을 가진) FAB 의 원 chrome — 합성되는 Button 으로 raw-forward 된다. Extended 는 원이 아니라 알약이라 폭이 라벨을 따라간다. 높이는 원형과 같은 [diameter] 이고, 좌우 패딩과 아이콘–라벨 간격만 사이즈별로 다르다([defaultsBySize]) — 라벨 크기는 CoreFabSize.toExtendedButtonSize 가 고른 Button 사이즈에서 상속받으므로 여기서 다시 진술하지 않는다. null (또는 설정되지 않은 필드)이면 [defaultsBySize] 의 해당 사이즈 값으로, 그다음 [defaultButtonStyle] 로 내려간다.
actionItemStyle CoreMenuItemStyle? Action-row chrome override — raw-forwarded to each composed MenuItem row. null (or any unset field) defers to [defaultActionItemStyle], which states only the hosted corner (8): otherwise the row is the MenuItem at CoreFabContract.actionItemSize as it draws itself. The row's leading icon, label typography, insets, gap and corner are this slot's leadingIconStyle / labelStyle / padding / contentGapStyle / borderRadius — the MenuItem 's own fields, not a Fab-side restatement.
actionButtonStyle CoreButtonStyle? Deprecated (since 0.164) — Use actionItemStyle — the action rows are MenuItem rows now, so their chrome is a CoreMenuItemStyle. The action-row chrome of the Button rows. Still honoured: [effectiveActionItemStyle] folds the fields a MenuItem row also has — padding , borderRadius , iconLabelGapStyle (→ contentGapStyle ), height (→ minHeight ), leadingIconStyle , labelStyle , backgroundColor , foregroundColor , hoverBackgroundColor — under [actionItemStyle], which wins field by field. Button-only fields (shadow, border, focus outline, …) have no row counterpart and are dropped.
actionLabelTextStyle CoreTextStyle? Deprecated (since 0.164) — Use actionItemStyle.labelStyle — the label belongs to the composed MenuItem row. The action-row label style of the Button rows. Still honoured as the row's labelStyle under [actionItemStyle] (see [effectiveActionItemStyle]).
actionIconStyle CoreIconStyle? Deprecated (since 0.164) — Use actionItemStyle.leadingIconStyle — the glyph belongs to the composed MenuItem row. The action-row leading icon style of the Button rows. Still honoured as the row's leadingIconStyle under [actionItemStyle] (see [effectiveActionItemStyle]).
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 — the single entry point for both, so a height / width in [buttonStyle] is overridden by this.
iconStyle CoreIconStyle? Main FAB icon style override — styles the icon glyph the FAB passes as the composed Button 's child (its size is the only source of the glyph size; a color pins the glyph colour). null defers to [defaultsBySize] for the active size. A leadingIconStyle stated in [buttonStyle] merges on top of this for every field except size .
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 .
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 Fab's own card.

크기 (Sizes)#

크기값지름(원형)아이콘Extended 높이Extended 좌우 패딩
Mini CoreFabSize.mini 48px 24px 48px 20px
Regular CoreFabSize.regular 56px 24px 56px 24px
Large CoreFabSize.large 72px 28px 72px 28px

Mini 가 48 인 것은 터치 타겟 하한이 48 이기 때문입니다 — FAB 은 그 하한을 깨면서까지 작아질 이유가 없습니다. Extended 는 높이가 원형과 같고 폭만 라벨을 따라 늘어납니다. 라벨 크기는 그 높이에 대응하는 Button 사이즈(lg / xl / xl2)에서 상속받으므로, 같은 높이의 Button 과 라벨이 갈리지 않습니다.

동작 스펙 (Behavior)#

인터랙션#

  • 클릭/탭: actions가 없으면 onPressed 콜백 실행. actions가 있으면 speed-dial을 펼치거나 접음
  • speed-dial 액션 클릭: 해당 액션의 onPressed 실행 후 speed-dial 자동 닫힘
  • 호버: 커서가 포인터로 전환
  • 포커스: 포커스 링 표시로 키보드 탐색 가능 상태 표시

Controlled / Uncontrolled#

  • onOpenChanged를 제공하지 않으면 Fab이 내부 상태로 speed-dial 열림/닫힘을 관리합니다 (uncontrolled)
  • onOpenChanged를 제공하면 open 값으로 외부에서 상태를 제어합니다 (controlled)

애니메이션#

  • 메뉴는 Popover 의 등장/퇴장 모션과 fabStyle 의 nested popover / popup 스타일을 사용합니다. 현재 액션별 stagger나 fan 원형 배열은 없습니다
  • expandDirection은 패널이 열릴 위치이며, 내부 행은 주어진 순서로 세로 배치합니다. fan은 up과 같은 위치입니다

메뉴 너비#

  • 패널 너비는 내용을 따르되 최소 160(layout/160) · 최대 280(layout/280) 사이로 묶이고, 행은 패널 너비를 채웁니다
  • 280 을 넘는 라벨은 줄바꿈하지 않고 한 줄로 말줄임(…)합니다 — 행 높이는 48 그대로입니다

패널 여백과 행 모서리#

  • 패널 안쪽 여백은 6 으로, Popup 의 목록 여백(defaultListPadding)입니다. Fab 이 여는 것은 행동 목록이라 Menu 와 같은 여백을 씁니다. 패널 반경은 Popup 의 패널 역할 값 12 입니다
  • 행 모서리는 8 입니다. 같은 판 안의 목록 행(ItemPicker · Autocomplete · PhoneInput · OptionItem)이 모두 8 이라 Fab 도 같은 값을 씁니다. MenuItem lg 가 혼자 설 때의 12 는 쓰지 않습니다

옛 행 스타일 필드 (Deprecated)#

메뉴 행이 Button(variant: .menu) 에서 MenuItem 으로 바뀌면서 행 chrome 의 진입점이 actionItemStyle(CoreMenuItemStyle) 하나가 됐습니다. 옛 필드는 0.164 부터 @Deprecated 이고 아직 동작합니다 — MenuItem 에 같은 뜻의 필드가 있으면 그쪽으로 옮겨 적용하고, 같은 필드를 actionItemStyle 이 말하면 actionItemStyle 이 이깁니다.

옛새
actionButtonStyle actionItemStyle ( padding · borderRadius · iconLabelGapStyle → contentGapStyle · height → minHeight · leadingIconStyle · labelStyle · 배경 · 전경 · hover 배경만 옮겨지고, 그림자 · 테두리 · 포커스 아웃라인 같은 Button 전용 필드는 버려집니다)
actionIconStyleactionItemStyle.leadingIconStyle
actionLabelTextStyleactionItemStyle.labelStyle
CoreFabContract.actionButtonSize CoreFabContract.actionItemSize (CoreMenuItemSize.lg)

사용 가이드라인 (Usage Guidelines)#

✅ Do#

한 화면에 하나의 FAB만 사용

Fab(
  trigger: const Icon(LucideIcons.plus),
  tooltip: '새 노트 작성',
  onPressed: handleCreateNote,
)

FAB은 화면에서 가장 중요한 단일 액션을 나타내야 합니다. 여러 개를 사용하면 사용자가 어느 것이 주요 액션인지 혼란스러워집니다. 이건 취향이 아니라 이 컴포넌트의 정의입니다 — 두 번째 FAB 을 놓고 싶어지는 순간은 그중 하나가 FAB 이 아니어야 한다는 신호이므로, 덜 중요한 쪽을 Button 으로 내리거나 둘을 하나의 메뉴 FAB(actions)으로 묶으세요.


✅ Do#

데스크톱에서는 Extended 를 쓴다

// 데스크톱 — 라벨이 붙은 알약
Fab(
  trigger: const Icon(LucideIcons.plus),
  label: const Text('New note'),
  onPressed: handleCreate,
)

포인터로 쓰는 넓은 화면에서 아이콘 하나만 떠 있으면 그 액션이 무엇인지 호버하기 전까지 알 수 없습니다. 툴팁은 알아내는 비용을 미룰 뿐 없애지 못합니다 — 가로 공간이 충분한 화면에서는 라벨을 직접 보여주는 편이 항상 낫습니다. 원형 FAB 은 라벨을 놓을 자리가 없는 좁은 화면의 형태입니다.


❌ Don't#

FAB 을 비활성화하지 않는다 — 숨긴다

// ❌ 쓸 수 없는 FAB 을 회색으로 띄워둔다
Fab(
  trigger: const Icon(LucideIcons.plus),
  onPressed: null,
)

// ✅ 쓸 수 없으면 자리에서 뺀다
if (canCreate)
  Fab(
    trigger: const Icon(LucideIcons.plus),
    onPressed: handleCreate,
  )

FAB 은 콘텐츠 위에 떠서 화면의 주요 액션 하나를 주장하는 형태입니다. 그 주장이 지금 유효하지 않다면 회색으로 남겨 자리를 차지할 이유가 없습니다 — 비활성 FAB 은 "여기 무언가 있는데 왜 안 되는지는 말하지 않겠다" 로 읽히고, 그 이유를 담을 자리가 FAB 에는 없습니다. 이유를 보여줘야 하는 상황이라면 그건 FAB 이 아니라 인라인 Button + 안내 문구의 일입니다.


❌ 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)#

역할#

플랫폼메인 FABspeed-dial 패널
Flutter CoUISemantics(expanded: …) 로 감싼 Button — Button 은 Clickable 을 통해 Semantics(button: true, enabled: …) 를 얹습니다 역할 없음. Popover 를 modal: false 로 합성하므로 배리어도 BlockSemantics 도 만들지 않습니다
Web native <button type="button"> . tooltip 이 있으면 aria-label , actions 가 있으면 aria-expanded Popover 가 트리거 래퍼 <div> 에 aria-haspopup="dialog" + aria-expanded 를, 패널 버블에 role="dialog" 를 얹습니다

expanded / aria-expanded 는 actions 가 비어 있지 않을 때만 붙습니다 — 아무것도 열지 않는 버튼에 false 를 붙이면 "접힘, 열려면 활성화" 로 읽히기 때문입니다. 액션 행은 양쪽 다 MenuItem 이고 (Web 은 role="menuitem"), Web 은 action.tooltip 이 있을 때만 aria-label 을 받습니다.

Fab 에는 enabled 파라미터가 없습니다. onPressed 를 null 로 두면 합성된 Button 이 비활성으로 내려가 Web 에서 disabled + aria-disabled="true" + tabindex="-1" 이 됩니다.

키보드#

키동작
Tab / Shift+Tab메인 FAB 로 이동 / 이탈
Enter · Space 메인 FAB 활성화 — actions 가 있으면 speed-dial 토글, 없으면 onPressed
Enter · Space (액션 행 위에서)그 액션 실행 후 speed-dial 닫기
EscapeWeb 에서만 speed-dial 닫기

액션 사이를 오가는 화살표 키는 구현되어 있지 않습니다. Flutter 는 Clickable 의 활성화 단축키 (SingleActivator(.enter) / SingleActivator(.space) → ActivateIntent)로, Web 은 native <button> 의 브라우저 기본 동작으로 활성화됩니다.

스크린 리더#

  • Extended FAB 는 label 텍스트가 이름이 됩니다. 아이콘 전용 FAB 의 이름·힌트 경로는 tooltip 입니다 — trigger 슬롯에 넣는 Icon 은 어느 플랫폼에서도 이름을 만들지 않습니다(Flutter 는 SvgPicture.string 을 semanticsLabel 없이, Web 은 텍스트 없는 <iconify-icon> 을 렌더합니다).
  • Web 의 tooltip 은 aria-label 로 나가 접근 가능한 이름 그 자체가 됩니다.
  • Flutter 의 tooltip 은 Tooltip 이 Semantics(tooltip: message) 로 얹어 이름이 아니라 힌트입니다.
  • 액션은 label 슬롯을 채우면 그 텍스트가 행 안에 렌더되어 읽히고, 아이콘만 넣으면 Web 은 action.tooltip → aria-label, Flutter 는 Tooltip 힌트로만 전달됩니다.
  • 열림/닫힘은 expanded / aria-expanded 로 통보되며, 라이브 리전은 어느 쪽에도 없습니다.

포커스 관리#

  • 진입: speed-dial 이 열려도 포커스는 메인 FAB 에 남습니다. Flutter 는 modal: false 라 전용 FocusScopeNode 를 만들지 않고 requestFocus() 도 modal 일 때만 실행하며, Web Popover 는 focus() 를 호출하는 경로가 없습니다.
  • 이탈: 닫을 때 트리거로 되돌리는 로직이 없습니다 — 애초에 포커스가 옮겨가지 않았으므로 메인 FAB 에 그대로 있습니다.
  • 트랩: 없습니다. Flutter 는 modal 배리어를 만들지 않아 배경이 시맨틱 트리에 남고, Web 도 패널이 Tab 을 가두지 않습니다.
  • 패널은 양 플랫폼 모두 오버레이 레이어에 그려집니다(Flutter Overlay, Web .popover portal). 그래서 액션 행이 트리거 바로 다음 tab 순서에 오지 않습니다 — 메인 FAB 에서 Tab 을 누르면 액션이 아니라 트리거 다음 요소로 갑니다.
  • 포커스 표시: 메인 FAB(원형 · extended 모두)의 링은 링과 FAB 사이 2px 간격을 surface 밴드로 칠합니다. FAB 는 스크롤하는 콘텐츠 위에 떠 있어 링이 어떤 색과 맞닿을지 알 수 없으므로, 링 + 밴드의 두 색 인디케이터로 어느 배경에서도 대비를 확보합니다. 기본값은 CoreFabStyle.defaultButtonStyle 의 focusOutlineStyle.offsetColor 에 있고, 다른 Button 의 간격은 투명입니다.

알려진 제약#

  • Flutter 에서 Escape 로 speed-dial 을 닫을 수 없습니다. Popover 는 Escape 를 modal && consumeOutsideTaps 일 때만 배선하는데 Fab 이 modal 을 넘기지 않아 기본값 false 로 남기 때문입니다. Web 은 document keydown 리스너라 포커스 위치와 무관하게 닫힙니다. 대안: controlled open + onOpenChanged 로 열림 상태를 호출부가 소유하고, 호출부가 자체 키 처리로 false 를 씁니다.
  • 아이콘만 넣은 FAB 는 Flutter 에서 이름 없이 "버튼" 으로만 읽힙니다. tooltip 은 힌트라 이름을 대신하지 못하고, trigger / action.icon 은 컴포넌트가 그대로 렌더하므로 안쪽에 이름을 심을 자리가 없습니다. 대안: 호출부가 Flutter 쪽에서 Semantics(label: …, child: Fab(…)) 로 감쌉니다. Web 은 tooltip 만 주면 해결됩니다.
  • Web 의 speed-dial 이 다이얼로그로 안내됩니다. 합성된 Popover 가 패널에 role="dialog" 를, 트리거에 aria-haspopup="dialog" 를 고정으로 얹습니다. 액션 목록에는 메뉴 계열 역할이 더 맞지만 Fab 이 이 두 값을 노출하지 않아 대안이 없습니다.

주 버튼의 크기 사다리는 다음과 같습니다:

  • Mini: 48px (중요도 낮은 보조 액션에만 사용 — 48 은 터치 타겟 하한이므로 그 아래로 줄이지 않습니다)
  • Regular: 56px (기본, 권장)
  • Large: 72px (특별히 강조가 필요한 경우)

크로스 플랫폼 차이점 (Platform Differences)#

항목FlutterWeb
클래스명FabFab
클릭 핸들러onPressedonPressed
elevation CoreShadow 토큰 → BoxShadow CoreShadow 토큰 → CSS box-shadow
speed-dial 토글 controlled open 상태 + 애니메이션 controlled open 상태 + CSS transition
메뉴 행 MenuItem (lg, 48px) MenuItem (lg, 48px, role="menuitem")
메뉴 패널 정렬FAB 끝 가장자리 (up · down)같음

메뉴 패널의 끝 정렬은 물리 오른쪽입니다(topEnd / bottomEnd). 오른쪽에서 왼쪽으로 쓰는 로케일에서도 패널은 뒤집히지 않고 FAB 의 오른쪽 끝에 맞춥니다 — 배치 어휘가 방향을 모릅니다. left · right 는 그대로 가운데 정렬입니다.

  • Button: 인라인 또는 덜 중요한 액션에 사용
  • Tooltip: 아이콘 전용 FAB에 레이블 설명 추가 시 함께 사용