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 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),
);
// 创建OverlayEntry
final overlayEntry = OverlayEntry(
builder: (context) {
return Stack(
children: [
// 禁止点击的遮罩层(条件渲染)
if (forbidClick == true)
Positioned.fill(
child: FadeTransition(
opacity: fadeAnimation,
child: Container(color: Colors.transparent),
),
),
// Toast内容
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,
),
),
],
),
),
),
),
),
),
],
);
},
);
var isRemoved = false;
late Future<bool> Function() hideThisToastImmediately;
Future<void> removeToast({
bool withAnimation = true,
bool triggerOnHide = true,
}) async {
if (isRemoved) {
return;
}
isRemoved = true;
if (_currentToastEntry == overlayEntry) {
_currentToastEntry = null;
}
if (_currentToastAnimationController == animationController) {
_currentToastAnimationController = null;
}
if (_currentToastFadeController == fadeController) {
_currentToastFadeController = null;
}
if (_currentToastHider == hideThisToastImmediately) {
_currentToastHider = null;
}
_activeToastHiders.remove(hideThisToastImmediately);
if (withAnimation) {
if (animationController.status != AnimationStatus.dismissed) {
await animationController.reverse();
}
if (forbidClick == true &&
fadeController.status != AnimationStatus.dismissed) {
await fadeController.reverse();
}
}
if (overlayEntry.mounted) {
overlayEntry.remove();
}
animationController.dispose();
fadeController.dispose();
if (triggerOnHide) {
onHide?.call();
}
}
hideThisToastImmediately = () async {
if (isRemoved) {
return false;
}
final isShowing =
animationController.status == AnimationStatus.forward ||
animationController.status == AnimationStatus.completed;
await removeToast(withAnimation: !isShowing, triggerOnHide: false);
return isShowing;
};
// 启动动画(先显示遮罩层)
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 previousHiders = isGlobalToastWhenShown
? List<Future<bool> Function()>.of(_activeToastHiders)
: const <Future<bool> Function()>[];
_activeToastHiders.add(hideThisToastImmediately);
if (isGlobalToastWhenShown) {
_currentToastEntry = overlayEntry;
_currentToastAnimationController = animationController;
_currentToastFadeController = fadeController;
_currentToastHider = hideThisToastImmediately;
}
// 插入到Overlay
overlay.insert(overlayEntry);
var skipAnimation = false;
for (final hider in previousHiders) {
skipAnimation = await hider() || skipAnimation;
}
if (skipAnimation) {
if (forbidClick == true) {
fadeController.value = 1;
}
animationController.value = 1;
return;
}
await startAnimations();
}
showToast();
// 延迟隐藏
Future.delayed(duration ?? const Duration(seconds: 1), () async {
await removeToast();
});
}