show static method

ScaffoldFeatureController<SnackBar, SnackBarClosedReason> show(
  1. BuildContext context, {
  2. required String title,
  3. String? subtitle,
  4. Color? background,
  5. Gradient? gradient,
  6. Duration? duration,
  7. IconData? icon,
  8. Color? iconColor,
  9. VoidCallback? onActionPressed,
  10. String? actionLabel,
  11. SnackbarType? type,
  12. SnackbarPosition? position,
  13. SnackbarAnimation? animation,
  14. SnackbarAnimationDirection? startAnimation,
  15. SnackbarAnimationDirection? endAnimation,
  16. bool? showProgressIndicator,
  17. bool hideLikeCircle = false,
  18. bool? dismissible,
  19. bool? enableHapticFeedback,
  20. bool? dismissOnTap,
  21. double? maxWidth,
  22. EdgeInsets? margin,
  23. BorderRadius? borderRadius,
  24. Color? borderColor,
  25. double? borderWidth,
  26. VoidCallback? onDismissed,
  27. VoidCallback? onTap,
  28. ContentDesignStyle? designStyle,
  29. double? blur,
  30. ImageFilter? backdropFilter,
})

Shows a modern floating snackbar with enhanced features.

Returns a ScaffoldFeatureController that can be used to programmatically dismiss the snackbar.

Throws an AssertionError if title is empty.

Example:

final controller = SavePointsSnackbar.show(
  context,
  title: 'Notification',
  subtitle: 'This is a snackbar',
  type: SnackbarType.info,
);

Implementation

static ScaffoldFeatureController<SnackBar, SnackBarClosedReason> show(
  BuildContext context, {
  required String title,
  String? subtitle,
  Color? background,
  Gradient? gradient,
  Duration? duration,
  IconData? icon,
  Color? iconColor,
  VoidCallback? onActionPressed,
  String? actionLabel,
  SnackbarType? type,
  SnackbarPosition? position,
  SnackbarAnimation? animation, // For backward compatibility
  SnackbarAnimationDirection? startAnimation,
  SnackbarAnimationDirection? endAnimation,
  bool? showProgressIndicator,
  bool hideLikeCircle = false,
  bool? dismissible,
  bool? enableHapticFeedback,
  bool? dismissOnTap,
  double? maxWidth,
  EdgeInsets? margin,
  BorderRadius? borderRadius,
  Color? borderColor,
  double? borderWidth,
  VoidCallback? onDismissed,
  VoidCallback? onTap,

  /// Design style: [ContentDesignStyle.solid] (filled) or [ContentDesignStyle.outlined] (light bg + border).
  ContentDesignStyle? designStyle,

  /// Optional blur sigma for glassmorphism behind the snackbar. When null, no blur is applied.
  double? blur,

  /// Optional custom [ImageFilter] for the backdrop. When set, overrides [blur].
  ImageFilter? backdropFilter,
}) {
  // Validate required parameters
  assert(title.isNotEmpty, 'Title cannot be empty');

  final config = SnackDiaBottomConfig().snackbar;
  final theme = Theme.of(context);
  final isDark = theme.brightness == Brightness.dark;
  final finalType = type ?? config.defaultType;
  final finalDesignStyle = designStyle ?? config.defaultDesignStyle;

  final colorConfig = SnackbarColorConfig(
    theme: theme,
    isDark: isDark,
    background: background,
    gradient: gradient,
    iconColor: iconColor,
    type: finalType,
    config: config,
    designStyle: finalDesignStyle,
  );

  final finalIcon = icon ?? colorConfig.defaultIcon;
  final finalIconColor = iconColor ?? colorConfig.iconColor;
  final finalPosition = position ?? config.defaultPosition;
  // Use new animation system if startAnimation or endAnimation is provided
  final finalAnimation = (startAnimation == null && endAnimation == null)
      ? (animation ?? config.defaultAnimation)
      : null;
  final finalDuration = duration ?? config.defaultDuration;
  final finalMargin =
      margin ?? config.defaultMargin ?? _getMargin(context, finalPosition);
  final finalBorderRadius =
      borderRadius ??
      config.defaultBorderRadius ??
      BorderRadius.circular(SnackbarConstants.borderRadius);
  final finalMaxWidth =
      maxWidth ?? config.maxWidth ?? SnackbarConstants.maxWidth;
  final finalShowProgressIndicator =
      showProgressIndicator ?? config.defaultShowProgressIndicator;
  final finalDismissible = dismissible ?? config.defaultDismissible;
  final finalEnableHapticFeedback =
      enableHapticFeedback ?? config.defaultEnableHapticFeedback;
  final finalDismissOnTap = dismissOnTap ?? config.defaultDismissOnTap;
  final finalBorderWidth = borderWidth ?? config.defaultBorderWidth;
  final finalBorderColor =
      borderColor ??
      config.defaultBorderColor ??
      finalIconColor.withValues(alpha: 0.3);

  if (finalEnableHapticFeedback) {
    HapticFeedback.lightImpact();
  }

  // Use overlay for top position (SnackBar doesn't support top positioning well)
  if (finalPosition == SnackbarPosition.top) {
    TopSnackbarOverlay.show(
      context,
      title: title,
      subtitle: subtitle,
      icon: finalIcon,
      iconColor: finalIconColor,
      backgroundColor: colorConfig.backgroundColor,
      gradient: colorConfig.gradient,
      duration: finalDuration,
      animation: finalAnimation,
      startAnimation: startAnimation,
      endAnimation: endAnimation,
      type: finalType,
      dismissible: finalDismissible,
      dismissOnTap: finalDismissOnTap,
      showProgressIndicator: finalShowProgressIndicator,
      borderRadius: finalBorderRadius,
      maxWidth: finalMaxWidth,
      borderColor: finalDesignStyle == ContentDesignStyle.outlined
          ? colorConfig.borderColor
          : (finalBorderWidth > 0 ? finalBorderColor : null),
      borderWidth: finalDesignStyle == ContentDesignStyle.outlined
          ? 2
          : finalBorderWidth,
      onTap: onTap,
      onDismissed: onDismissed,
      blur: blur,
      backdropFilter: backdropFilter,
      designStyle: finalDesignStyle,
      titleColor: colorConfig.titleColor,
      subtitleColor: colorConfig.subtitleColor,
      showCloseButton:
          finalDesignStyle == ContentDesignStyle.outlined ||
              finalDesignStyle == ContentDesignStyle.leftAccent ||
              finalDesignStyle == ContentDesignStyle.tonal ||
              finalDismissible,
    );
    // Return a dummy controller for top position. The overlay shows the real
    // toast at top; we show a minimal SnackBar on-screen (required by Flutter
    // to avoid "Floating SnackBar presented off screen") with 1ms duration
    // so it dismisses immediately and is barely visible.
    final dummyMargin = _getMargin(context, SnackbarPosition.bottom);
    return ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: const SizedBox.shrink(),
        duration: const Duration(milliseconds: 1),
        behavior: SnackBarBehavior.floating,
        backgroundColor: Colors.transparent,
        elevation: 0,
        margin: dummyMargin,
      ),
    );
  }

  // Remove any existing snackbar before showing a new one
  final scaffoldMessenger = ScaffoldMessenger.of(context);
  scaffoldMessenger.hideCurrentSnackBar();

  // Use standard SnackBar for bottom position
  final controller = scaffoldMessenger.showSnackBar(
    SnackBar(
      behavior: .floating,
      duration: finalDuration,
      margin: finalMargin,
      shape: RoundedRectangleBorder(
        borderRadius: finalBorderRadius,
        side:
            finalDesignStyle != ContentDesignStyle.outlined &&
                finalBorderWidth > 0
            ? BorderSide(color: finalBorderColor, width: finalBorderWidth)
            : BorderSide.none,
      ),
      backgroundColor: Colors.transparent,
      elevation: 0,
      dismissDirection: finalDismissible
          ? DismissDirection.horizontal
          : DismissDirection.none,
      content: ModernSnackbarContent(
        title: title,
        subtitle: subtitle,
        icon: finalIcon,
        iconColor: finalIconColor,
        backgroundColor: colorConfig.backgroundColor,
        gradient: colorConfig.gradient,
        showProgressIndicator: finalShowProgressIndicator,
        duration: finalDuration,
        type: finalType,
        position: finalPosition,
        animation: finalAnimation ?? SnackbarAnimation.fadeSlide,
        borderRadius: finalBorderRadius,
        maxWidth: finalMaxWidth,
        dismissOnTap: finalDismissOnTap,
        onTap: onTap,
        blur: blur,
        backdropFilter: backdropFilter,
        designStyle: finalDesignStyle,
        titleColor: colorConfig.titleColor,
        subtitleColor: colorConfig.subtitleColor,
        borderColor: finalDesignStyle == ContentDesignStyle.outlined
            ? colorConfig.borderColor
            : null,
        showCloseButton:
            finalDesignStyle == ContentDesignStyle.outlined ||
                finalDesignStyle == ContentDesignStyle.leftAccent ||
                finalDesignStyle == ContentDesignStyle.tonal ||
                finalDismissible,
      ),
      action: onActionPressed != null && actionLabel != null
          ? SnackBarAction(
              label: actionLabel.toUpperCase(),
              textColor: Colors.white,
              onPressed: () {
                if (finalEnableHapticFeedback) {
                  HapticFeedback.mediumImpact();
                }
                ScaffoldMessenger.of(context).hideCurrentSnackBar();
                onActionPressed();
              },
            )
          : null,
    ),
  );

  controller.closed.then((reason) {
    onDismissed?.call();
  });

  return controller;
}