如何使用flutter_animate为Navigator.pop()添加反向淡出动画?
实现全屏覆盖层的淡入/淡出双向动画(flutter_animate)
要实现关闭时的反向淡出动画,无需单独处理pop逻辑,只需把动画整合到PageRouteBuilder的transitionsBuilder中,让路由系统自动管理正向(打开)和反向(关闭)的动画流程。
修改后的完整代码
import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatelessWidget { const MyHomePage({super.key, required this.title}); final String title; @override Widget build(BuildContext context) { return Scaffold( body: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'Animated Transition', ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.of(context).push( PageRouteBuilder( opaque: false, transitionDuration: const Duration(milliseconds: 300), reverseTransitionDuration: const Duration(milliseconds: 300), pageBuilder: (BuildContext context, _, __) => const AddOverlay(), // 核心:通过transitionsBuilder绑定路由动画 transitionsBuilder: (context, animation, secondaryAnimation, child) { return Animate( adapter: AnimationAdapter(animation), effects: const [FadeEffect()], child: child, ); }, ), ); }, child: const Icon(Icons.add), ), ); } } class AddOverlay extends StatelessWidget { const AddOverlay({super.key}); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black.withOpacity(0.85), body: BackdropFilter( filter: ImageFilter.blur( sigmaX: 5.0, sigmaY: 5.0, ), child: Center( child: ElevatedButton( onPressed: () { Navigator.of(context).pop(); // 直接pop,路由自动触发反向动画 }, child: const Text("Close"))), ), ); } }
关键修改说明
- 将动画从
pageBuilder的子组件移到transitionsBuilder中,通过AnimationAdapter(animation)绑定路由自带的动画对象。路由打开时animation正向执行(0→1)触发淡入,关闭时自动反向执行(1→0)触发淡出。 - 设置
transitionDuration和reverseTransitionDuration统一动画时长(可选,使用默认值也可)。 - 关闭时只需正常调用
Navigator.of(context).pop(),无需额外处理动画逻辑,路由系统会自动反向播放过渡动画。
备选方案(手动控制动画)
如果需要在动画过程中添加自定义逻辑,可以给AddOverlay添加AnimationController,先播放反向动画再执行pop:
class AddOverlay extends StatefulWidget { const AddOverlay({super.key}); @override State<AddOverlay> createState() => _AddOverlayState(); } class _AddOverlayState extends State<AddOverlay> with SingleTickerProviderStateMixin { late final AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, )..forward(); // 初始化时播放淡入动画 } @override void dispose() { _controller.dispose(); super.dispose(); } void _closeOverlay() { _controller.reverse().then((_) { if (mounted) Navigator.of(context).pop(); }); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black.withOpacity(0.85), body: BackdropFilter( filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), child: Animate( controller: _controller, effects: const [FadeEffect()], child: Center( child: ElevatedButton( onPressed: _closeOverlay, child: const Text("Close"), ), ), ), ), ); } }
该方案适合需要额外动画逻辑的场景,但第一种结合路由过渡的方式更简洁,符合Flutter路由动画的设计规范。
内容的提问来源于stack exchange,提问作者Lucas Chae
相关产品推荐
相关产品推荐

