176 lines
5.3 KiB
Dart
176 lines
5.3 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter/scheduler.dart';
|
||
|
||
enum ToastType { success, error, warn, info, loading }
|
||
|
||
// 自定义Toast工具类 - 气泡式提示
|
||
class ToastUtils {
|
||
static OverlayEntry? _overlayEntry;
|
||
static bool _isShowing = false;
|
||
|
||
/// 显示气泡提示
|
||
/// [context] 上下文
|
||
/// [message] 提示文本
|
||
/// [backgroundColor] 背景色
|
||
/// [duration] 显示时长
|
||
/// [positionTop] 距离顶部的高度
|
||
static void showToast(
|
||
BuildContext context,
|
||
String message, {
|
||
Color backgroundColor = const Color(0xCC333333),
|
||
Duration duration = const Duration(seconds: 1),
|
||
double positionTop = 80.0,
|
||
}) {
|
||
// 先关闭正在显示的提示
|
||
if (_isShowing) {
|
||
_overlayEntry?.remove();
|
||
_isShowing = false;
|
||
}
|
||
|
||
// 创建OverlayEntry
|
||
_overlayEntry = OverlayEntry(
|
||
builder: (BuildContext context) => Positioned(
|
||
top: positionTop,
|
||
left: MediaQuery.of(context).size.width * 0.1,
|
||
right: MediaQuery.of(context).size.width * 0.1,
|
||
child: Material(
|
||
color: Colors.transparent,
|
||
child: Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||
decoration: BoxDecoration(
|
||
color: backgroundColor,
|
||
borderRadius: BorderRadius.circular(18),
|
||
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 10, offset: const Offset(0, 2))],
|
||
),
|
||
child: Text(
|
||
message,
|
||
textAlign: TextAlign.center,
|
||
style: const TextStyle(color: Colors.white, fontSize: 14, height: 1.2),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
|
||
// 显示提示
|
||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||
Overlay.of(context).insert(_overlayEntry!);
|
||
_isShowing = true;
|
||
|
||
// 定时关闭
|
||
Future.delayed(duration, () {
|
||
if (_isShowing) {
|
||
_overlayEntry?.remove();
|
||
_isShowing = false;
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
/// 显示成功提示(绿色背景 + 85%不透明度)
|
||
static void showSuccess(BuildContext context, String message) {
|
||
showToast(
|
||
context,
|
||
message,
|
||
// 绿色:0xFF00C853 → 调整为 0xD900C853(D9=85%不透明度)
|
||
backgroundColor: const Color(0xD900C853),
|
||
duration: const Duration(seconds: 1),
|
||
);
|
||
}
|
||
|
||
/// 显示错误提示(红色背景 + 85%不透明度)
|
||
static void showError(BuildContext context, String message, {int seconds = 1}) {
|
||
showToast(
|
||
context,
|
||
message,
|
||
// 红色:0xFFFF5252 → 调整为 0xD9FF5252(D9=85%不透明度)
|
||
backgroundColor: const Color(0xD9FF5252),
|
||
duration: Duration(seconds: seconds),
|
||
);
|
||
}
|
||
|
||
/// 显示普通提示(灰色背景 + 80%不透明度)
|
||
static void showInfo(BuildContext context, String message) {
|
||
showToast(
|
||
context,
|
||
message,
|
||
// 灰色:0xCC333333 → 调整为 0xCC333333(保持80%,也可改为 0xB3333333=70%)
|
||
backgroundColor: const Color(0xCC333333),
|
||
duration: const Duration(seconds: 1),
|
||
);
|
||
}
|
||
|
||
/// 显示警告提示(黄色背景 + 85%不透明度)
|
||
static void showWarn(BuildContext context, String message) {
|
||
showToast(
|
||
context,
|
||
message,
|
||
// 黄色:0xFFFFC107 → 调整为 0xD9FFC107(D9=85%不透明度)
|
||
backgroundColor: const Color(0xD9FFC107),
|
||
duration: const Duration(seconds: 1),
|
||
);
|
||
}
|
||
|
||
/// 显示加载提示(蓝色背景 + 85%不透明度)
|
||
static void showLoading(BuildContext context, String message) {
|
||
showToast(
|
||
context,
|
||
message,
|
||
// 蓝色:0xFF0688D8 → 调整为 0xD90688D8(D9=85%不透明度)
|
||
backgroundColor: const Color(0xD90688D8),
|
||
duration: const Duration(seconds: 1),
|
||
);
|
||
}
|
||
|
||
// 扩展:支持自定义文字颜色
|
||
static void showToastWithTextColor(
|
||
BuildContext context,
|
||
String message, {
|
||
Color backgroundColor = const Color(0xCC333333),
|
||
Color textColor = Colors.white,
|
||
Duration duration = const Duration(seconds: 2),
|
||
double positionTop = 80.0,
|
||
}) {
|
||
if (_isShowing) {
|
||
_overlayEntry?.remove();
|
||
_isShowing = false;
|
||
}
|
||
|
||
_overlayEntry = OverlayEntry(
|
||
builder: (BuildContext context) => Positioned(
|
||
top: positionTop,
|
||
left: MediaQuery.of(context).size.width * 0.1,
|
||
right: MediaQuery.of(context).size.width * 0.1,
|
||
child: Material(
|
||
color: Colors.transparent,
|
||
child: Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||
decoration: BoxDecoration(
|
||
color: backgroundColor,
|
||
borderRadius: BorderRadius.circular(24),
|
||
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 10, offset: const Offset(0, 2))],
|
||
),
|
||
child: Text(
|
||
message,
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(color: textColor, fontSize: 14, height: 1.2),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
|
||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||
Overlay.of(context).insert(_overlayEntry!);
|
||
_isShowing = true;
|
||
|
||
Future.delayed(duration, () {
|
||
if (_isShowing) {
|
||
_overlayEntry?.remove();
|
||
_isShowing = false;
|
||
}
|
||
});
|
||
});
|
||
}
|
||
}
|