show static method
void
show({
- BuildContext? context,
- String? message,
- Widget? child,
- Widget? icon,
- Duration? fadeDuration,
- Duration? duration,
- double? fontSize,
- Color? textColor,
- Color? backgroundColor,
- EdgeInsets? padding,
- BorderRadius? borderRadius,
- BetterToastPosition position = BetterToastPosition.bottom,
- double? topOffset,
- double? bottomOffset,
- TextAlign? textAlign = TextAlign.center,
- bool? forbidClick = false,
- double? width,
- double? height,
- VoidCallback? onHide,
Implementation
static void show({
BuildContext? context,
/// Toast 的消息
String? message,
/// Toast 的子组件
Widget? child,
/// Toast 的图标
Widget? icon,
/// 动画持续时间
Duration? fadeDuration,
/// Toast 的持续时间
Duration? duration,
/// Toast 的字体大小
double? fontSize,
Color? textColor,
/// Toast 的背景颜色
Color? backgroundColor,
/// Toast 的内边距
EdgeInsets? padding,
/// Toast 的圆角半径
BorderRadius? borderRadius,
/// Toast 的位置
BetterToastPosition position = BetterToastPosition.bottom,
/// 顶部偏移量
double? topOffset,
/// 底部偏移量
double? bottomOffset,
/// 文本对齐方式
TextAlign? textAlign = TextAlign.center,
/// 是否禁止点击
bool? forbidClick = false,
/// Toast 的宽度
double? width,
/// Toast 的高度
double? height,
VoidCallback? onHide,
}) {
final overlay = _getOverlay(context);
if (overlay == null) {
return;
}
final screenHeight = BetterScreenUtil.screenHeight;
topOffset ??= screenHeight * 0.2;
bottomOffset ??= screenHeight * 0.2;
final toastData = _BetterToastData(
message: message,
child: child,
icon: icon,
fontSize: fontSize,
textColor: textColor,
backgroundColor: backgroundColor,
padding: padding,
borderRadius: borderRadius,
position: position,
topOffset: topOffset,
bottomOffset: bottomOffset,
textAlign: textAlign,
forbidClick: forbidClick,
width: width,
height: height,
);
if (isGlobalToast) {
final toastDuration = duration ?? const Duration(milliseconds: 1000);
final currentToastHandle = _currentToastHandle;
if (currentToastHandle != null && currentToastHandle.isActive) {
currentToastHandle.update(
data: toastData,
duration: toastDuration,
onHide: onHide,
);
return;
}
late _BetterToastHandle toastHandle;
toastHandle = _BetterToastHandle(
overlay: overlay,
data: toastData,
fadeDuration: fadeDuration ?? const Duration(milliseconds: 250),
duration: toastDuration,
onHide: onHide,
onRemove: () {
if (_currentToastHandle == toastHandle) {
_currentToastHandle = null;
}
},
);
_currentToastHandle = toastHandle;
toastHandle.show();
return;
}
final isGlobalToastWhenShown = isGlobalToast;
// 动画控制器
final animationController = AnimationController(
vsync: overlay,
duration: fadeDuration ?? const Duration(milliseconds: 250),
);
// 遮罩层控制器(仅当 forbidClick=true 时使用)
final fadeController = AnimationController(
vsync: overlay,
duration: const Duration(milliseconds: 150),
);
final fadeAnimation = CurvedAnimation(
parent: fadeController,
curve: Curves.easeInOut,
);
// 位移动画
final offsetAnimation =
Tween<Offset>(
begin: position == BetterToastPosition.center
? Offset.zero
: Offset(0, position == BetterToastPosition.bottom ? 0.1 : -0.1),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: animationController,
curve: Curves.easeOutQuad,
),
);
// 透明度动画
final toastOpacityAnimation = Tween<double>(begin: 0, end: 1).animate(
CurvedAnimation(parent: animationController, curve: Curves.easeIn),
);
Widget buildToastContent() {
return Align(
alignment: _getAlignment(position),
child: SlideTransition(
position: offsetAnimation,
child: FadeTransition(
opacity: toastOpacityAnimation,
child: Material(
color: Colors.transparent,
child:
child ??
Container(
width: width,
height: height,
margin: EdgeInsets.only(
top: position == BetterToastPosition.top
? (topOffset ?? 0)
: 0,
bottom: position == BetterToastPosition.bottom
? (bottomOffset ?? 0)
: 0,
),
constraints: BoxConstraints(
maxWidth: width ?? BetterScreenUtil.screenWidth * 0.8,
),
padding:
padding ??
EdgeInsets.symmetric(horizontal: 12.bw, vertical: 8.bw),
decoration: BoxDecoration(
color: backgroundColor ?? Colors.black.withAlpha(178),
borderRadius: borderRadius ?? BorderRadius.circular(8.bw),
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (icon != null) icon,
if (icon != null && message != null)
SizedBox(height: 8.bw),
if (message != null)
Text(
message,
textAlign: textAlign,
style: TextStyle(
color: textColor ?? Colors.white,
fontSize: fontSize ?? 14.bsp,
),
),
],
),
),
),
),
),
);
}
// 创建OverlayEntry
final overlayEntry = OverlayEntry(
builder: (context) {
return Stack(
children: [
// 禁止点击的遮罩层(条件渲染)
if (forbidClick == true)
Positioned.fill(
child: FadeTransition(
opacity: fadeAnimation,
child: Container(color: Colors.transparent),
),
),
// Toast内容
IgnorePointer(
ignoring: forbidClick != true,
child: buildToastContent(),
),
],
);
},
);
var isRemoved = false;
var isDisposed = false;
Timer? autoHideTimer;
late Future<bool> Function() hideThisToastImmediately;
Future<void> removeToast({
bool withAnimation = true,
bool triggerOnHide = true,
}) async {
if (isRemoved) {
return;
}
isRemoved = true;
autoHideTimer?.cancel();
autoHideTimer = null;
if (_currentToastHider == hideThisToastImmediately) {
_currentToastHider = null;
}
if (withAnimation && !isDisposed) {
try {
if (animationController.status != AnimationStatus.dismissed) {
await animationController.reverse();
}
if (forbidClick == true &&
fadeController.status != AnimationStatus.dismissed) {
await fadeController.reverse();
}
} on TickerCanceled {
// Toast may be replaced while its exit animation is running.
}
}
if (overlayEntry.mounted) {
overlayEntry.remove();
}
if (!isDisposed) {
animationController.dispose();
fadeController.dispose();
isDisposed = true;
}
if (triggerOnHide) {
onHide?.call();
}
}
hideThisToastImmediately = () async {
if (isRemoved) {
return false;
}
final isShowing =
animationController.status == AnimationStatus.forward ||
animationController.status == AnimationStatus.completed;
await removeToast(withAnimation: false, triggerOnHide: false);
return isShowing;
};
void scheduleAutoHide() {
if (isRemoved || isDisposed) {
return;
}
autoHideTimer = Timer(duration ?? const Duration(milliseconds: 1000), () {
removeToast();
});
}
// 启动动画(先显示遮罩层)
Future<void> startAnimations() async {
try {
if (forbidClick == true) {
await fadeController.forward();
}
await animationController.forward();
} on TickerCanceled {
// Toast may be replaced before its entrance animation completes.
}
}
Future<void> showToast() async {
final previousHider = isGlobalToastWhenShown ? _currentToastHider : null;
if (isGlobalToastWhenShown) {
_currentToastHider = hideThisToastImmediately;
}
// 插入到Overlay
overlay.insert(overlayEntry);
final skipAnimation = await previousHider?.call() ?? false;
if (isRemoved || isDisposed) {
return;
}
if (skipAnimation) {
if (forbidClick == true) {
fadeController.value = 1;
}
animationController.value = 1;
scheduleAutoHide();
return;
}
await startAnimations();
scheduleAutoHide();
}
showToast();
}