Files
superport_v2/lib/widgets/components/feedback.dart
2025-09-29 01:51:47 +09:00

132 lines
3.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
/// Superport 전역에서 사용하는 토스트/스낵바 헬퍼.
class SuperportToast {
SuperportToast._();
static void success(BuildContext context, String message) {
_show(context, message, _ToastVariant.success);
}
static void info(BuildContext context, String message) {
_show(context, message, _ToastVariant.info);
}
static void warning(BuildContext context, String message) {
_show(context, message, _ToastVariant.warning);
}
static void error(BuildContext context, String message) {
_show(context, message, _ToastVariant.error);
}
static void _show(
BuildContext context,
String message,
_ToastVariant variant,
) {
final theme = ShadTheme.of(context);
final (Color background, Color foreground) = switch (variant) {
_ToastVariant.success => (
theme.colorScheme.primary,
theme.colorScheme.primaryForeground,
),
_ToastVariant.info => (
theme.colorScheme.accent,
theme.colorScheme.accentForeground,
),
_ToastVariant.warning => (
theme.colorScheme.secondary,
theme.colorScheme.secondaryForeground,
),
_ToastVariant.error => (
theme.colorScheme.destructive,
theme.colorScheme.destructiveForeground,
),
};
final messenger = ScaffoldMessenger.of(context);
messenger
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Text(
message,
style: theme.textTheme.small.copyWith(
color: foreground,
fontWeight: FontWeight.w600,
),
),
backgroundColor: background,
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 3),
),
);
}
}
enum _ToastVariant { success, info, warning, error }
/// 기본 골격을 표현하는 스켈레톤 블록.
class SuperportSkeleton extends StatelessWidget {
const SuperportSkeleton({
super.key,
this.width,
this.height = 16,
this.borderRadius = const BorderRadius.all(Radius.circular(8)),
});
final double? width;
final double height;
final BorderRadius borderRadius;
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
return AnimatedContainer(
duration: const Duration(milliseconds: 600),
decoration: BoxDecoration(
color: theme.colorScheme.muted,
borderRadius: borderRadius,
),
width: width,
height: height,
);
}
}
/// 리스트 데이터를 대체하는 반복 스켈레톤 레이아웃.
class SuperportSkeletonList extends StatelessWidget {
const SuperportSkeletonList({
super.key,
this.itemCount = 6,
this.height = 56,
this.gap = 12,
this.padding = const EdgeInsets.all(16),
});
final int itemCount;
final double height;
final double gap;
final EdgeInsetsGeometry padding;
@override
Widget build(BuildContext context) {
return Padding(
padding: padding,
child: Column(
children: [
for (var i = 0; i < itemCount; i++) ...[
SuperportSkeleton(
height: height,
borderRadius: BorderRadius.circular(10),
),
if (i != itemCount - 1) SizedBox(height: gap),
],
],
),
);
}
}