From 77b2effb34f9a315f518c7167d42d5903e384c38 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 22 Nov 2025 19:18:42 +0800 Subject: [PATCH] :dizzy: Update the animation of alert's dialog --- lib/widgets/alert.dart | 83 +++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/lib/widgets/alert.dart b/lib/widgets/alert.dart index 57d47940..2012fa3b 100644 --- a/lib/widgets/alert.dart +++ b/lib/widgets/alert.dart @@ -39,36 +39,51 @@ OverlayEntry? _loadingOverlay; GlobalKey<_FadeOverlayState> _loadingOverlayKey = GlobalKey(); class _FadeOverlay extends StatefulWidget { - const _FadeOverlay({super.key, required this.child}); - final Widget child; + const _FadeOverlay({ + super.key, + this.child, + this.builder, + this.duration = const Duration(milliseconds: 200), + this.curve = Curves.linear, + }) : assert(child != null || builder != null); + + final Widget? child; + final Widget Function(BuildContext, Animation)? builder; + final Duration duration; + final Curve curve; @override State<_FadeOverlay> createState() => _FadeOverlayState(); } -class _FadeOverlayState extends State<_FadeOverlay> { - bool _visible = false; +class _FadeOverlayState extends State<_FadeOverlay> + with SingleTickerProviderStateMixin { + late AnimationController _controller; @override void initState() { super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - setState(() => _visible = true); - }); + _controller = AnimationController(vsync: this, duration: widget.duration); + _controller.forward(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); } Future animateOut() async { - setState(() => _visible = false); - await Future.delayed(const Duration(milliseconds: 200)); + await _controller.reverse(); } @override Widget build(BuildContext context) { - return AnimatedOpacity( - opacity: _visible ? 1.0 : 0.0, - duration: const Duration(milliseconds: 200), - child: widget.child, - ); + final animation = CurvedAnimation(parent: _controller, curve: widget.curve); + if (widget.builder != null) { + return widget.builder!(context, animation); + } + return FadeTransition(opacity: animation, child: widget.child); } } @@ -166,18 +181,36 @@ Future showOverlayDialog({ builder: (context) => _FadeOverlay( key: key, - child: Stack( - children: [ - Positioned.fill( - child: GestureDetector( - onTap: barrierDismissible ? () => close(null) : null, - behavior: HitTestBehavior.opaque, - child: const ColoredBox(color: Colors.black54), + duration: const Duration(milliseconds: 150), + curve: Curves.easeOut, + builder: (context, animation) { + return Stack( + children: [ + Positioned.fill( + child: FadeTransition( + opacity: animation, + child: GestureDetector( + onTap: barrierDismissible ? () => close(null) : null, + behavior: HitTestBehavior.opaque, + child: const ColoredBox(color: Colors.black54), + ), + ), ), - ), - Center(child: builder(context, close)), - ], - ), + Center( + child: SlideTransition( + position: Tween( + begin: const Offset(0, 0.05), + end: Offset.zero, + ).animate(animation), + child: FadeTransition( + opacity: animation, + child: builder(context, close), + ), + ), + ), + ], + ); + }, ), );