284 lines
7.1 KiB
Dart
284 lines
7.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
|
|
enum SnackbarType {
|
|
success,
|
|
error,
|
|
warning,
|
|
info,
|
|
}
|
|
|
|
class SnackbarUtil {
|
|
static ColorScheme get _scheme {
|
|
final ctx = Get.context;
|
|
if (ctx == null) {
|
|
return const ColorScheme.light();
|
|
}
|
|
return Theme.of(ctx).colorScheme;
|
|
}
|
|
|
|
/// Show a success snackbar
|
|
static void showSuccess(
|
|
String message, {
|
|
String? title,
|
|
Duration? duration,
|
|
}) {
|
|
_showSnackbar(
|
|
title: title ?? 'Success',
|
|
message: message,
|
|
type: SnackbarType.success,
|
|
duration: duration,
|
|
);
|
|
}
|
|
|
|
/// Show an error snackbar
|
|
static void showError(
|
|
String message, {
|
|
String? title,
|
|
Duration? duration,
|
|
}) {
|
|
_showSnackbar(
|
|
title: title ?? 'Error',
|
|
message: message,
|
|
type: SnackbarType.error,
|
|
duration: duration,
|
|
);
|
|
}
|
|
|
|
/// Show a warning snackbar
|
|
static void showWarning(
|
|
String message, {
|
|
String? title,
|
|
Duration? duration,
|
|
}) {
|
|
_showSnackbar(
|
|
title: title ?? 'Warning',
|
|
message: message,
|
|
type: SnackbarType.warning,
|
|
duration: duration,
|
|
);
|
|
}
|
|
|
|
/// Show an info snackbar
|
|
static void showInfo(
|
|
String message, {
|
|
String? title,
|
|
Duration? duration,
|
|
}) {
|
|
_showSnackbar(
|
|
title: title ?? 'Info',
|
|
message: message,
|
|
type: SnackbarType.info,
|
|
duration: duration,
|
|
);
|
|
}
|
|
|
|
/// Internal method to show snackbar with custom styling
|
|
static void _showSnackbar({
|
|
required String title,
|
|
required String message,
|
|
required SnackbarType type,
|
|
Duration? duration,
|
|
}) {
|
|
final config = _getSnackbarConfig(type);
|
|
|
|
Get.snackbar(
|
|
title,
|
|
message,
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: config.backgroundColor,
|
|
colorText: config.textColor,
|
|
icon: Icon(
|
|
config.icon,
|
|
color: config.iconColor,
|
|
size: 28.w,
|
|
),
|
|
shouldIconPulse: true,
|
|
borderRadius: 12.r,
|
|
margin: EdgeInsets.all(16.w),
|
|
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
|
|
duration: duration ?? const Duration(seconds: 3),
|
|
isDismissible: true,
|
|
dismissDirection: DismissDirection.horizontal,
|
|
forwardAnimationCurve: Curves.easeOutBack,
|
|
reverseAnimationCurve: Curves.easeInBack,
|
|
animationDuration: const Duration(milliseconds: 500),
|
|
boxShadows: [
|
|
BoxShadow(
|
|
color: config.backgroundColor.withOpacity(0.3),
|
|
blurRadius: 12,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
titleText: Text(
|
|
title,
|
|
style: TextStyle(
|
|
fontSize: 16.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: config.textColor,
|
|
),
|
|
),
|
|
messageText: Text(
|
|
message,
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: config.textColor.withOpacity(0.9),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Get configuration based on snackbar type
|
|
static _SnackbarConfig _getSnackbarConfig(SnackbarType type) {
|
|
final scheme = _scheme;
|
|
switch (type) {
|
|
case SnackbarType.success:
|
|
return _SnackbarConfig(
|
|
backgroundColor: scheme.primary,
|
|
textColor: scheme.onPrimary,
|
|
iconColor: scheme.onPrimary,
|
|
icon: Icons.check_circle_rounded,
|
|
);
|
|
case SnackbarType.error:
|
|
return _SnackbarConfig(
|
|
backgroundColor: scheme.error,
|
|
textColor: scheme.onError,
|
|
iconColor: scheme.onError,
|
|
icon: Icons.error_rounded,
|
|
);
|
|
case SnackbarType.warning:
|
|
return _SnackbarConfig(
|
|
backgroundColor: scheme.tertiary,
|
|
textColor: scheme.onTertiary,
|
|
iconColor: scheme.onTertiary,
|
|
icon: Icons.warning_rounded,
|
|
);
|
|
case SnackbarType.info:
|
|
return _SnackbarConfig(
|
|
backgroundColor: scheme.secondary,
|
|
textColor: scheme.onSecondary,
|
|
iconColor: scheme.onSecondary,
|
|
icon: Icons.info_rounded,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Show a loading snackbar that can be dismissed programmatically
|
|
static void showLoading(String message) {
|
|
final scheme = _scheme;
|
|
Get.snackbar(
|
|
'Loading',
|
|
message,
|
|
snackPosition: SnackPosition.BOTTOM,
|
|
backgroundColor: scheme.inverseSurface,
|
|
colorText: scheme.onInverseSurface,
|
|
icon: SizedBox(
|
|
width: 24.w,
|
|
height: 24.h,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 2,
|
|
valueColor: AlwaysStoppedAnimation<Color>(scheme.onInverseSurface),
|
|
),
|
|
),
|
|
shouldIconPulse: false,
|
|
borderRadius: 12.r,
|
|
margin: EdgeInsets.all(16.w),
|
|
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
|
|
duration: const Duration(days: 1), // Long duration
|
|
isDismissible: false,
|
|
showProgressIndicator: false,
|
|
);
|
|
}
|
|
|
|
/// Dismiss the current snackbar
|
|
static void dismiss() {
|
|
if (Get.isSnackbarOpen) {
|
|
Get.closeCurrentSnackbar();
|
|
}
|
|
}
|
|
|
|
/// Show a custom snackbar with action button
|
|
static void showWithAction({
|
|
required String title,
|
|
required String message,
|
|
required String actionLabel,
|
|
required VoidCallback onAction,
|
|
SnackbarType type = SnackbarType.info,
|
|
Duration? duration,
|
|
}) {
|
|
final config = _getSnackbarConfig(type);
|
|
|
|
Get.snackbar(
|
|
title,
|
|
message,
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: config.backgroundColor,
|
|
colorText: config.textColor,
|
|
icon: Icon(
|
|
config.icon,
|
|
color: config.iconColor,
|
|
size: 28.w,
|
|
),
|
|
shouldIconPulse: true,
|
|
borderRadius: 12.r,
|
|
margin: EdgeInsets.all(16.w),
|
|
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
|
|
duration: duration ?? const Duration(seconds: 5),
|
|
isDismissible: true,
|
|
mainButton: TextButton(
|
|
onPressed: () {
|
|
Get.closeCurrentSnackbar();
|
|
onAction();
|
|
},
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: Colors.white,
|
|
backgroundColor: Colors.white.withOpacity(0.2),
|
|
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8.r),
|
|
),
|
|
),
|
|
child: Text(
|
|
actionLabel,
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
titleText: Text(
|
|
title,
|
|
style: TextStyle(
|
|
fontSize: 16.sp,
|
|
fontWeight: FontWeight.bold,
|
|
color: config.textColor,
|
|
),
|
|
),
|
|
messageText: Text(
|
|
message,
|
|
style: TextStyle(
|
|
fontSize: 14.sp,
|
|
color: config.textColor.withOpacity(0.9),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Internal configuration class for snackbar styling
|
|
class _SnackbarConfig {
|
|
final Color backgroundColor;
|
|
final Color textColor;
|
|
final Color iconColor;
|
|
final IconData icon;
|
|
|
|
_SnackbarConfig({
|
|
required this.backgroundColor,
|
|
required this.textColor,
|
|
required this.iconColor,
|
|
required this.icon,
|
|
});
|
|
}
|
|
|