Skip to content

LayrzSnackbar

Kenny Mochizuki Escalona edited this page Aug 13, 2026 · 2 revisions

LayrzSnackbar

A Material-free snackbar component displaying transient feedback messages in a stacked overlay with title, description, severity typing, and optional custom styling.

Metadata
Mirrors: ThemedSnackbar and ThemedSnackbarMessenger (layrz_theme)
Phase: M5 (Feedback layer)
Domain: Feedback
Primitive: Built on Overlay and OverlayEntry (Flutter SDK)
Status: Confirmed scope.


Overview

LayrzSnackbar is a feedback component that renders a single transient notification. It is always displayed and managed through the LayrzSnackbarMessenger ancestor widget, which owns the overlay stack, queue, and dismiss logic.

Design Principles

  • Title and description always paired: both titleText and descriptionText are mandatory string parameters (no Widget labels).
  • Severity typing: a type enum drives icon and color selection, defaulting to success.
  • Custom styling contract: icon and color parameters are only valid when type is .custom; supplying them for any other type triggers a debug assertion.
  • External lifecycle: the caller supplies the snackbar instance to the messenger; the messenger owns stacking, animation, and dismissal.
  • Stack-based rendering: multiple simultaneous snackbars stack vertically, displayed on the top right (desktop/tablet) or adapted positioning (mobile).

API Structure

Core Data Model

// Design sketch — illustrative only
class LayrzSnackbar {
  /// The human-readable title displayed at the top of the snackbar.
  ///
  /// Required. No Widget parameter is supported; only plain text.
  final String titleText;

  /// The descriptive message content shown below the title.
  ///
  /// Required. This is the primary feedback content.
  final String descriptionText;

  /// The severity type driving color and icon selection.
  ///
  /// Defaults to `.success`. One of: .success, .error, .warning, .info, .context, .custom.
  /// When .custom, [icon] and [color] must both be supplied.
  /// For any non-custom type, [icon] and [color] must be null.
  final LayrzSnackbarType type;

  /// Custom icon displayed in the snackbar.
  ///
  /// Only valid when [type] is .custom. Must be null for any other type.
  /// A debug assertion enforces this contract.
  final IconData? icon;

  /// Custom background color for the snackbar.
  ///
  /// Only valid when [type] is .custom. Must be null for any other type.
  /// A debug assertion enforces this contract.
  final Color? color;

  /// Duration for which the snackbar is displayed before auto-dismissing.
  ///
  /// Defaults to 10 seconds. Must be greater than zero.
  final Duration duration;

  /// Callback invoked when the snackbar is tapped.
  ///
  /// When supplied, tapping the snackbar invokes this callback AND dismisses the snackbar.
  /// If not supplied, the snackbar is not directly tappable (dismissal is via timeout or manual close affordance).
  final VoidCallback? onTap;

  /// Whether the snackbar displays a manual dismiss affordance (close button).
  ///
  /// Defaults to true. When true, a close icon button is rendered, allowing manual dismissal
  /// independent of the timeout.
  final bool isDismissible;
}

ThemedSnackbarType Enum

The type enum defines six severity categories, each with a standard color and icon from the theme:

enum LayrzSnackbarType {
  /// Default type; requires [icon] and [color] to be supplied.
  custom,

  /// Success notification: green background, checkmark-like icon.
  success,

  /// Error notification: red background, error icon.
  error,

  /// Warning notification: orange background, warning icon.
  warning,

  /// Informational notification: blue background, info icon.
  info,

  /// Contextual notification: theme-dependent background.
  context,
}

LayrzSnackbarMessenger

LayrzSnackbarMessenger is an ancestor widget that hosts the overlay layer and manages the snackbar stack. It should wrap the entire app or a major section of the widget tree.

// Design sketch — illustrative only
class LayrzSnackbarMessenger extends StatefulWidget {
  /// The child widget tree that will use the snackbar system.
  final Widget child;

  /// Maximum width for the snackbar display.
  ///
  /// If not supplied, a sensible default is used (e.g., 400 logical pixels).
  final double? maxWidth;

  /// Padding around the snackbar stack.
  ///
  /// Defaults to EdgeInsets.all(16). Controls spacing from screen edges.
  final EdgeInsetsGeometry padding;

  /// Whether snackbars should be rendered with accessibility announcements.
  ///
  /// Defaults to true.
  final bool enableAccessibility;
}

To show a snackbar:

// Design sketch — illustrative only
LayrzSnackbarMessenger.of(context).show(
  LayrzSnackbar(
    titleText: 'Changes Saved',
    descriptionText: 'Your profile has been updated successfully.',
    type: .success,
  ),
);

Stacking and Queue Behavior

When multiple snackbars are shown without dismissal, they stack vertically on the screen. The exact positioning and stacking order is determined by the messenger's configuration:

  • Desktop / Tablet: Snackbars are stacked on the top-right corner of the screen, growing downward as new snackbars are added.
  • Mobile: Positioning is adapted to the available screen size; may stack differently based on device dimensions.
  • Order: Newer snackbars are stacked below (or above) existing ones — the exact order is an implementation detail and will be finalized during M5 implementation.

When a snackbar's duration expires, it auto-dismisses and is removed from the stack. Remaining snackbars remain visible.


Dismissal Affordances

A snackbar can be dismissed in three ways:

  1. Timeout: When the duration expires, the snackbar automatically dismisses.
  2. Manual close button: When isDismissible = true, a close icon (✕) is rendered on the snackbar. Tapping it dismisses the snackbar immediately.
  3. Tap callback: When onTap is supplied, tapping the snackbar invokes the callback and dismisses the snackbar.

Icon and Color Contract

When type is .custom:

  • Both icon and color must be supplied (non-null).
  • A debug assertion enforces: assert(icon != null && color != null, 'Custom snackbar requires both icon and color').

When type is any non-custom value (.success, .error, .warning, .info, .context):

  • Both icon and color must be null.
  • A debug assertion enforces: assert(icon == null && color == null, 'Non-custom type must have null icon and color').

This contract ensures clarity: either the type drives the appearance, or the caller explicitly provides both dimensions.


Dependencies

  • M1 Theme System (LayrzTheme, LayrzThemeData) — color tokens for non-custom types.
  • Flutter SDK Overlay (Overlay, OverlayEntry) — rendering surface and entry/exit animation.

Open Questions

  • Stacking direction and order: Should newer snackbars appear above or below existing ones? Is there a maximum stack size before older ones are auto-dismissed?

  • Animation timing and curve: What is the entry/exit animation duration and Curve? The layrz_theme constant kSnackbarAnimationDuration = 300ms is available for reference, but the animation profile (curve type) is TBD.

  • Accessibility announcements: Should snackbars trigger Semantics announcements on entry (e.g., onShow)? If yes, should the announcement include both title and description, or only one?

  • Pause on hover: Does the auto-dismiss timeout pause when the user hovers over the snackbar on desktop? Or does it continue counting down?

  • Icon and color source for non-custom types: Are icons sourced from layrz_icons? If so, which specific icon is paired with each severity type?

  • Touch target size: When isDismissible = true, what is the minimum tap target size for the close button? Should it meet Material's 48×48 dp minimum?


Design Reference

Critical path item: A design reference (Figma, annotated screenshot, or visual spec) must be attached to the LayrzSnackbar component before implementation begins. This specification must cover:

  • Snackbar chrome: padding, border radius, shadow (if any).
  • Title and description typography and spacing.
  • Icon sizing and placement.
  • Close button affordance size and position (when isDismissible = true).
  • Background color and contrast for all six type variants in light and dark themes.
  • Entry/exit animation (duration, curve, direction).
  • Stacking layout (vertical spacing between multiple snackbars).
  • Mobile adaptation (screen size < tablet breakpoint).
  • Hover state on desktop (if applicable).
  • Accessibility: focus indicators, announcements, keyboard navigation.

Last updated: 2026-08-13
Related documents: Input Contract, Design Tokens, Architecture

Clone this wiki locally