Toast | CoUI
LogoCoUI

Toast

ํ† ์ŠคํŠธ ์•Œ๋ฆผ ์ปดํฌ๋„ŒํŠธ

Toast#

์‚ฌ์šฉ์ž์—๊ฒŒ ๊ฐ„๋‹จํ•œ ํ”ผ๋“œ๋ฐฑ ๋ฉ”์‹œ์ง€๋ฅผ ์ผ์‹œ์ ์œผ๋กœ ํ‘œ์‹œํ•˜๋Š” ํ† ์ŠคํŠธ ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.

Live Preview#

์‚ฌ์šฉ ์‹œ๊ธฐ (When to Use)#

์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”:

  • ์ž‘์—… ๊ฒฐ๊ณผ(์„ฑ๊ณต/์‹คํŒจ)๋ฅผ ๊ฐ„๋‹จํžˆ ์•Œ๋ฆด ๋•Œ
  • ๋˜๋Œ๋ฆฌ๊ธฐ(undo) ์•ก์…˜์„ ์ œ๊ณตํ•  ๋•Œ
  • ์‚ฌ์šฉ์ž ํ๋ฆ„์„ ๋ฐฉํ•ดํ•˜์ง€ ์•Š๋Š” ๋น„์นจ์ž…์  ์•Œ๋ฆผ์ด ํ•„์š”ํ•  ๋•Œ

๋Œ€์‹  ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”:

  • Dialog: ์‚ฌ์šฉ์ž ํ™•์ธ์ด ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•œ ์ค‘์š”ํ•œ ์•Œ๋ฆผ์ผ ๋•Œ
  • Alert / Banner: ํŽ˜์ด์ง€์— ๊ณ ์ •๋œ ์˜๊ตฌ์  ์•Œ๋ฆผ์ด ํ•„์š”ํ•  ๋•Œ
  • Tooltip: ํŠน์ • ์š”์†Œ์— ๋Œ€ํ•œ ์„ค๋ช…์ด ํ•„์š”ํ•  ๋•Œ

๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ• (Basic Usage)#

// ๊ธฐ๋ณธ ํ† ์ŠคํŠธ ์นด๋“œ
Toast(
  message: '์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
  description: '๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
  onDismiss: () {},
)

// destructive ๋ณ€ํ˜•
Toast(
  message: '์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.',
  description: '๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์„ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.',
  variant: CoreToastVariant.destructive,
  onDismiss: () {},
)

// showToast์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ
showToast(
  context: context,
  builder: (context, overlay) => Toast(
    message: 'ํŒŒ์ผ์ด ์—…๋กœ๋“œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
    onDismiss: overlay.close,
  ),
)
// ๊ธฐ๋ณธ ํ† ์ŠคํŠธ (default ๋ณ€ํ˜•)
Toast(
  message: '์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
  description: '๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
  onDismiss: handleDismiss,
)

// destructive ๋ณ€ํ˜•
Toast(
  message: '์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.',
  description: '๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์„ ํ™•์ธํ•ด ์ฃผ์„ธ์š”.',
  variant: CoreToastVariant.destructive,
  onDismiss: handleDismiss,
)

Props / Parameters#

์†์„ฑํƒ€์ž…๊ธฐ๋ณธ๊ฐ’์„ค๋ช…
messageStringํ•„์ˆ˜ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€
description String? null ๋ณด์กฐ ์„ค๋ช… ํ…์ŠคํŠธ
variant CoreToastVariant CoreToastVariant.defaultVariant (neutral) ์‹œ๊ฐ์  ๋ณ€ํ˜• (์‹œ๋งจํ‹ฑ โ€” ์œ„์ ฏ ํŒŒ๋ผ๋ฏธํ„ฐ)
onDismiss VoidCallback? / CoreVoidCallback? null ๋‹ซ๊ธฐ ๋ฒ„ํŠผ ์ฝœ๋ฐฑ (null์ด๋ฉด ๋‹ซ๊ธฐ ๋ฒ„ํŠผ ์—†์Œ)
action Widget? / Component? null ์•ก์…˜ ์Šฌ๋กฏ ์œ„์ ฏ
toastStyle CoreToastStyle? null panel chrome / nested ์Šฌ๋กฏ ๋ฌถ์Œ

๋น ๋ฅธ ์˜ค๋ฒ„๋ผ์ด๋“œ (Chain)#

์ด๋ฏธ ๋งŒ๋“  Toast ์ธ์Šคํ„ด์Šค์— ์Šคํƒ€์ผ์„ ๋น ๋ฅด๊ฒŒ ๋ง๋ถ™์ด๊ณ  ์‹ถ๋‹ค๋ฉด withStyle ์ฒด์ธ์„ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ์ž์˜ style ์Šฌ๋กฏ ์ธ์ž์™€ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•˜์ง€๋งŒ, ์ด๋ฏธ ๊ตฌ์„ฑ๋œ ์œ„์ ฏ ์œ„์—์„œ ๋ฐ”๋กœ ์ด์–ด ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

class ToastChainExample extends StatelessWidget {
  const ToastChainExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Toast(
      message: 'Success',
      description: 'Your changes have been saved.',
      onDismiss: () {},
    ).withStyle(
      const CoreToastStyle(
        panelBorderRadius: CoreBorderRadius.all(CoreRadius.radius4),
        panelBorderColor: CoreColor.token(CoreColors.primary),
        panelBorderWidth: CoreStrokeWidth.stroke2,
        panelPaddingWithDismiss: CoreEdgeInsets.directional(
          start: CoreSpace.space16,
          top: CoreSpace.space16,
          end: CoreSpace.space32,
          bottom: CoreSpace.space16,
        ),
        messageDescriptionGapStyle: CoreGapStyle(size: CoreSpace.space8),
      ),
    );
  }
}
class ToastChainExample extends StatelessComponent {
  const ToastChainExample({super.key});

  @override
  Component build(BuildContext context) {
    return Toast(
      message: 'Success',
      description: 'Your changes have been saved.',
      onDismiss: () {},
    ).withStyle(
      const CoreToastStyle(
        panelBorderRadius: CoreBorderRadius.all(CoreRadius.radius4),
        panelBorderColor: CoreColor.token(CoreColors.primary),
        panelBorderWidth: CoreStrokeWidth.stroke2,
        panelPaddingWithDismiss: CoreEdgeInsets.directional(
          start: CoreSpace.space16,
          top: CoreSpace.space16,
          end: CoreSpace.space32,
          bottom: CoreSpace.space16,
        ),
        messageDescriptionGapStyle: CoreGapStyle(size: CoreSpace.space8),
      ),
    );
  }
}

์Šคํƒ€์ผ ์‹œ์Šคํ…œ โ€” toastStyle#

Toast ์˜ panel chrome / ์Šฌ๋กฏ ๋ฏธ์„ธ ์กฐ์ •์€ ๋‹จ์ผ toastStyle (CoreToastStyle) ์œผ๋กœ ํ๋ฆ…๋‹ˆ๋‹ค. ์‹œ๋งจํ‹ฑ enum (variant) ์€ ์œ„์ ฏ ํŒŒ๋ผ๋ฏธํ„ฐ.

Toast(
  message: '์ €์žฅ ์™„๋ฃŒ',
  description: '๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์ ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค',
  variant: CoreToastVariant.destructive,
  onDismiss: handleDismiss,
  action: Button(
    variant: .ghost,
    onPressed: undo,
    child: Text('๋˜๋Œ๋ฆฌ๊ธฐ'),
  ),
  toastStyle: CoreToastStyle(
    panelBackgroundColor: CoreColor.token(CoreColors.surface),
    panelBorderColor: CoreColor.token(CoreColors.outline),
    panelBorderRadius: CoreBorderRadius.all(CoreRadius.radius12),
    panelPadding: CoreEdgeInsets.all(CoreSpace.space16),
    contentActionGapStyle: CoreGapStyle(size: CoreSpace.space12),
    messageStyle: CoreTextStyle.token(
      CoreTextStyles.labelLarge,
    ).copyWith(fontWeight: CoreFontWeight.semiBold),
    descriptionStyle: CoreTextStyle.token(
      CoreTextStyles.bodySmall,
      color: CoreColor.token(CoreColors.onSurfaceVariant),
    ),
    // actionButtonStyle ์€ action ์•ˆ์˜ Button ์ด ์ž๋™ ์ ์šฉ ๊ฐ€๋Šฅ
    // (์œ„์ฒ˜๋Ÿผ variant ์ง์ ‘ ์ฃผ์ž… โ€” asChild)
  ),
)

CoreToastStyle ํ•„๋“œ#

ํ•„๋“œํƒ€์ž…์„ค๋ช…
panelBackgroundColor CoreColor? Panel background fill colour override.
panelBorderColor CoreColor? Panel border stroke colour override.
panelBorderRadius CoreBorderRadius? Panel border radius override.
panelBorderWidth double? Panel border stroke width override (logical px). When null, defers to [defaultPanelBorderWidth] (1).
panelPadding CoreEdgeInsets? Panel content padding override (no dismiss button). When null, defers to [defaultPanelPadding].
panelPaddingWithDismiss CoreEdgeInsets? Panel content padding override when a dismiss button is present โ€” the resolver selects this (instead of [panelPadding]) so the trailing edge can leave room for the close button. When null, defers to [defaultPanelPaddingWithDismiss].
panelBoxShadow List<CoreShadowLayer>? Panel elevation shadow override. When null, defers to [defaultPanelBoxShadow].
minHeight double? Panel minimum height override (logical px). When null, defers to [defaultMinHeight].
contentActionGapStyle CoreGapStyle? Gap style between the message + description column and the action slot โ€” nested [CoreGapStyle] slot forwarded to the Gap widget that separates the content column from the action. null defers to [defaultContentActionGapStyle].
iconStyle CoreIconStyle? Leading-glyph style override. null defers to [defaultIconStyle]. Size and any other icon chrome only โ€” the colour comes from the variant, because the glyph is what tells the three variants apart.
iconContentGapStyle CoreGapStyle? Gap between the leading glyph and the content column. null defers to [defaultIconContentGapStyle]. Reached only by the variants that draw a glyph; a toast with none starts at its content.
messageDescriptionGapStyle CoreGapStyle? Gap style between the message and description rows inside the content column โ€” nested [CoreGapStyle] slot forwarded to the Gap widget that separates message from description. null defers to [defaultMessageDescriptionGapStyle].
closeInset CoreEdgeInsets? Close-button inset from the top-right corner โ€” four-sided so the caller can shift the dismiss button on any axis. This is panel layout (it positions the dismiss button inside the panel padding region), not button chrome, so it stays flat; the button's own chrome lives in the nested [closeButtonStyle] slot. When null, defers to [defaultCloseInset].
transitionDuration Duration? Panel chrome transition duration override. When null, defers to [defaultTransitionDuration].
messageStyle CoreTextStyle? Message (primary text) style override.
descriptionStyle CoreTextStyle? Description (secondary text) style override.
actionButtonStyle CoreButtonStyle? Action button style override. To change the action's variant , inject the action widget directly via Toast(action: Button(variant: outline,...)) rather than packing variant into this style โ€” ์›์น™ 8 (asChild pattern). No default* on this field, deliberately โ€” a flat default would name one colour for three variants; what the resolver injects underneath this slot is instead the VARIANT's own foreground, the same way [defaultCloseButtonStyle] is tinted with closeForeground : text CoreButtonStyle(foregroundColor: variant.foregroundColor, hoverForegroundColor: variant.foregroundColor).merge(actionButtonStyle) // this slot.merge(action.buttonStyle) // the caller's own Button, wins It used to inject nothing, on the reasoning that the action is the caller's widget and its chrome should pass through untouched. That reasoning is exactly what made the action unreadable: a Button carries a page-surface foreground, and the toast is inverse-surface , so plain / link measured 2.1 : 1 against the slab and outline / ghost / text โ€” whose onSurface is the very same token as the slab โ€” 1.0 : 1, invisible. A toast is the one surface a caller's button was never designed against, so the toast has to say what legible means there; the caller's explicit style still wins by merge order, and the caller's own variant is still theirs (asChild).
closeButtonStyle CoreButtonStyle? Close (dismiss) button style override. The single entry point for the dismiss-button chrome โ€” padding / close-glyph ( leadingIconStyle ) / hover transition ( animationDuration ) / focus-ring width ( focusOutlineStyle.borderWidth ). Merged onto [defaultCloseButtonStyle] and raw-forwarded to Button(variant:.plain, buttonStyle: โ€ฆ) (the plain variant owns the resting-opacity / hover-fade / foreground semantics).

CoreToastStyle ๋ณ€ํ˜•๋ณ„ ๊ธฐ๋ณธ๊ฐ’ (CoreToastVariantStyle)#

ํ•„๋“œ success destructive neutral
backgroundColor inverseSurface inverseSurface inverseSurface
closeForeground inverseOnSurface (opacity defaultCloseForegroundOpacity) inverseOnSurface (opacity defaultCloseForegroundOpacity) inverseOnSurface (opacity defaultCloseForegroundOpacity)
closeHoverForeground inverseOnSurface inverseOnSurface inverseOnSurface
foregroundColor inverseOnSurface inverseOnSurface inverseOnSurface
borderColor inverseSurface inverseSurface inverseSurface
descriptionForeground inverseOnSurface (opacity defaultDescriptionForegroundOpacity) inverseOnSurface (opacity defaultDescriptionForegroundOpacity) inverseOnSurface (opacity defaultDescriptionForegroundOpacity)
iconColorsuccesserrorโ€”

asChild ํŒจํ„ด โ€” action ๋ฒ„ํŠผ ๋ณ€๊ฒฝ#

action ๋ฒ„ํŠผ์˜ ์‹œ๋งจํ‹ฑ (variant) ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•  ๋•Œ๋Š” action ์Šฌ๋กฏ์— ์ง์ ‘ ์œ„์ ฏ์„ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค:

Toast(
  message: '...',
  action: Button(variant: .ghost, child: Text('๋˜๋Œ๋ฆฌ๊ธฐ')),  // โ† variant ์ง์ ‘
)

toastStyle.actionButtonStyle ์€ chrome ๋ฏธ์„ธ ์กฐ์ •์šฉ (paddingH / labelStyle ๋“ฑ).

Resolve chain#

design system default for variant
  โ†’ CoreToastTheme.style                       // ํ”„๋กœ์ ํŠธ ๊ณตํ†ต
  โ†’ CoreToastTheme.variantStyles[widget.variant]   // variant ๋ณ„
  โ†’ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ์Šฌ๋กฏ ์˜ค๋ฒ„๋ผ์ด๋“œ
  โ†’ widget.toastStyle                          // ์ธ์Šคํ„ด์Šค๋ณ„

๋ณ€ํ˜• (Variants)#

์„ธ ๋ณ€ํ˜•์€ ์„ ํ–‰ ๊ธ€๋ฆฌํ”„ ํ•˜๋‚˜๋กœ๋งŒ ๊ฐˆ๋ฆฝ๋‹ˆ๋‹ค. ๋ฐฐ๊ฒฝ ยท ์ „๊ฒฝ ยท ๋ณด๋”๋Š” ์…‹ ๋‹ค ๊ฐ™์€ inverse-surface ์Œ์ด๋ผ(์œ„ ๋ณ€ํ˜•๋ณ„ ๊ธฐ๋ณธ๊ฐ’ ํ‘œ ์ฐธ์กฐ), ๋ณ€ํ˜•์„ ๊ณ ๋ฅด๋Š” ๊ฒƒ์€ ์ƒ‰์„ ๊ณ ๋ฅด๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ฌด์—‡์„ ์ฃผ์žฅํ•˜๋Š”์ง€๋ฅผ ๊ณ ๋ฅด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

neutral#

๊ธฐ๋ณธ๊ฐ’์ž…๋‹ˆ๋‹ค. ๊ธ€๋ฆฌํ”„๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค โ€” ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ ํ•˜์ง€ ์•Š๋Š” ์•Œ๋ฆผ.

variant ๋ฅผ ์ ์ง€ ์•Š์œผ๋ฉด ์ด๊ฒƒ์ด ๊ทธ๋ ค์ง‘๋‹ˆ๋‹ค. ์•„๋ฌด ๋ณ€ํ˜•๋„ ์ด๋ฆ„ํ•˜์ง€ ์•Š์€ ํ˜ธ์ถœ์ž๋Š” ์–ด๋–ค ๊ฒฐ๊ณผ๋„ ์ฃผ์žฅํ•˜์ง€ ์•Š์€ ๊ฒƒ์ด๊ณ , ๊ธ€๋ฆฌํ”„ ์—†๋Š” ํ† ์ŠคํŠธ๊ฐ€ ๊ทธ ์ƒํƒœ์˜ ๋ชจ์Šต์ž…๋‹ˆ๋‹ค.

Toast(
  message: 'New version available',
  description: 'Reload to get the latest.',
  onDismiss: handleDismiss,
)

success#

์ดˆ๋ก ์„ฑ๊ณต ์ฒดํฌ๋ฅผ ๋‹ต๋‹ˆ๋‹ค. ๋ฌด์–ธ๊ฐ€ ์„ฑ๊ณตํ–ˆ์„ ๋•Œ๋งŒ ์”๋‹ˆ๋‹ค.

Toast(
  message: 'Saved',
  description: 'Your changes have been saved.',
  variant: CoreToastVariant.success,
  onDismiss: handleDismiss,
)

default_ ๋Š” ์ด ๋ณ€ํ˜•์˜ ์˜› ์ด๋ฆ„์ด๊ณ  deprecated ๋ณ„์นญ์œผ๋กœ ๋‚จ์•„ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ’์œผ๋กœ ์“ฐ๋Š” ์ฝ”๋“œ๋Š” ๊ทธ๋Œ€๋กœ ์ปดํŒŒ์ผ๋˜์ง€๋งŒ, switch ์˜ exhaustive arm ์€ success ๋กœ ๋ฐ”๊ฟ”์•ผ ํ•ฉ๋‹ˆ๋‹ค.

destructive#

์˜ค๋ฅ˜ ์ƒ‰ ๊ธ€๋ฆฌํ”„๋ฅผ ๋‹ต๋‹ˆ๋‹ค. ์‹คํŒจ๋ฅผ ๋ณด๊ณ ํ•  ๋•Œ ์”๋‹ˆ๋‹ค.

Toast(
  message: 'Error',
  description: 'Something went wrong.',
  variant: CoreToastVariant.destructive,
  onDismiss: handleDismiss,
)

Flutter ์ธํ”„๋ผ (ToastLayer)#

Flutter์—์„œ๋Š” ToastLayer์™€ showToast()๋กœ ํ† ์ŠคํŠธ ํ‘œ์‹œ/์Šคํƒœํ‚น/์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

// ์•ฑ ์ตœ์ƒ๋‹จ์— ToastLayer ๋ฐฐ์น˜
ToastLayer(
  maxStackedEntries: 3,
  expandMode: ExpandMode.expandOnHover,
  child: MyApp(),
)

// ํ† ์ŠคํŠธ ํ‘œ์‹œ
final overlay = showToast(
  context: context,
  builder: (context, overlay) => Toast(
    message: '์ฒ˜๋ฆฌ ์™„๋ฃŒ',
    onDismiss: overlay.close,
  ),
  location: ToastLocation.bottomRight,
  showDuration: Duration(seconds: 5),
);

// ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ๋‹ซ๊ธฐ
overlay.close();

ํ…Œ๋งˆ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•#

CoreComponentTheme(
  toast: CoreToastTheme(
    style: CoreToastStyle(
      panelBorderRadius: CoreBorderRadius.all(CoreRadius.radius12),
    ),
  ),
)

์‚ฌ์šฉ ๊ฐ€์ด๋“œ๋ผ์ธ (Usage Guidelines)#

โœ… Do#

action ์˜ ์‹œ๋งจํ‹ฑ(variant)์€ ์Šฌ๋กฏ์— ์ง์ ‘ ์ฃผ์ž… (asChild)

Toast(
  message: '์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
  action: Button(variant: .ghost, onPressed: undo, child: Text('๋˜๋Œ๋ฆฌ๊ธฐ')),
  onDismiss: handleDismiss,
)

์ด์œ : toastStyle.actionButtonStyle์€ paddingยท๋ผ๋ฒจ ์Šคํƒ€์ผ ๊ฐ™์€ chrome ๋ฏธ์„ธ ์กฐ์ • ์ „์šฉ์ž…๋‹ˆ๋‹ค. ๋ฒ„ํŠผ์˜ variant ์ž์ฒด๋ฅผ ๋ฐ”๊พธ๋ ค๋ฉด action ์Šฌ๋กฏ์— ์›ํ•˜๋Š” Button์„ ์ง์ ‘ ๋„ฃ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


โŒ Don't#

Toast ๋ฅผ ToastLayer/showToast() ์—†์ด ํ™”๋ฉด์— ์ง์ ‘ ๋ฐฐ์น˜ํ•˜์ง€ ์•Š๊ธฐ (Flutter)

// โŒ Toast ์นด๋“œ๋งŒ ํŠธ๋ฆฌ ์–ด๋”˜๊ฐ€์— ๊ทธ๋Œ€๋กœ ๋‘  โ€” ์Šคํƒœํ‚น/์ž๋™ ๋‹ซํž˜/์• ๋‹ˆ๋ฉ”์ด์…˜ ์—†์Œ
Column(children: [Toast(message: '์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.', onDismiss: () {})])

์ด์œ : Flutter์—์„œ ํ† ์ŠคํŠธ์˜ ํ‘œ์‹œยท์Šคํƒœํ‚น(maxStackedEntries)ยท๋“ฑ์žฅ/ํ‡ด์žฅ ์• ๋‹ˆ๋ฉ”์ด์…˜์€ ToastLayer + showToast() ์ธํ”„๋ผ๊ฐ€ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. Toast ์นด๋“œ๋งŒ ํŠธ๋ฆฌ์— ๋‘๋ฉด ์—ฌ๋Ÿฌ ํ† ์ŠคํŠธ๊ฐ€ ๊ฒน์น˜๊ฑฐ๋‚˜, ์œ„์น˜(ToastLocation)์™€ ์ง€์† ์‹œ๊ฐ„(showDuration)์ด ์ „ํ˜€ ๋ฐ˜์˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ ‘๊ทผ์„ฑ (Accessibility)#

์Šคํฌ๋ฆฐ ๋ฆฌ๋”#

  • Flutter: Semantics(liveRegion: true) ์ ์šฉ์œผ๋กœ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”์— ์ž๋™ ์•Œ๋ฆผ
  • Web: role="status", aria-live="polite" ์ ์šฉ์œผ๋กœ ๋น„์นจ์ž…์  ์•Œ๋ฆผ

๋‹ซ๊ธฐ ๋ฒ„ํŠผ#

  • onDismiss๊ฐ€ ์ œ๊ณต๋˜๋ฉด aria-label="Close" ์†์„ฑ์ด ์žˆ๋Š” ๋‹ซ๊ธฐ ๋ฒ„ํŠผ ๋ Œ๋”๋ง

ํฌ๋กœ์Šค ํ”Œ๋žซํผ ์ฐจ์ด์  (Platform Differences)#

ํ•ญ๋ชฉFlutterWeb
ํ† ์ŠคํŠธ ์นด๋“œToast (ํ†ต์ผ)Toast (ํ†ต์ผ)
์ธํ”„๋ผToastLayer + showToast()์™ธ๋ถ€ ๊ด€๋ฆฌ
์Šคํƒœํ‚น๋‚ด์žฅ (hover ํ™•์žฅ, ์ตœ๋Œ€ ๊ฐœ์ˆ˜)์™ธ๋ถ€ ๊ด€๋ฆฌ
๋‹ซ๊ธฐ ๋ฐฉ์‹์Šค์™€์ดํ”„ + ํด๋ฆญ + ํ”„๋กœ๊ทธ๋ž˜๋ฐ๋‹ซ๊ธฐ ๋ฒ„ํŠผ ํด๋ฆญ
  • Dialog: ์‚ฌ์šฉ์ž ํ™•์ธ์ด ํ•„์š”ํ•œ ๋ชจ๋‹ฌ ์•Œ๋ฆผ
  • Alert: ํŽ˜์ด์ง€์— ๊ณ ์ •๋œ ์•Œ๋ฆผ
  • Banner: ํŽ˜์ด์ง€ ์ƒ๋‹จ์˜ ์ „์ฒด ๋„ˆ๋น„ ์•Œ๋ฆผ