Flutter自定义淡入淡出路由实现侧滑返回淡入淡出动画
问题描述
我实现了一个CustomPageRoute,push新页面时的淡入动画正常,但侧滑返回时始终无法使用淡入淡出动画,试过多种方案,最接近的效果还是会出现页面滑入/滑出。我需要push和pop(包括侧滑返回)都只使用淡入淡出动画。
我的Flutter淡入过渡代码如下:
class FadePageTransition extends Page { final Widget page; const FadePageTransition({ required this.page, LocalKey? key, String? restorationId, }) : super( key: key, restorationId: restorationId, ); @override Route createRoute(BuildContext context) { return FadeRoute( child: page, routeSettings: this, ); } } class FadeRoute<T> extends PageRoute<T> { final Widget child; final RouteSettings routeSettings; FadeRoute({ required this.child, required this.routeSettings, }); @override RouteSettings get settings => routeSettings; @override Color? get barrierColor => Palette.black; @override String? get barrierLabel => null; @override Widget buildPage( BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, ) { return FadeTransition( opacity: animation, child: child, ); } @override bool get maintainState => true; @override Duration get transitionDuration => const Duration( milliseconds: transitionDurationInMS, ); }
另外,iOS Swift中实现类似功能的参考代码:
// 处理下拉手势 @objc private func handlePan(gestureRecognizer:UIPanGestureRecognizer) { // 根据用户滑动距离计算进度 let translation = panGR.translation(in: nil) let progress = translation.y / 2 / view.bounds.height switch panGR.state { case .began: // 正常开始过渡 self.dismissView() break case .changed: Hero.shared.update(progress) default: // 根据进度和触摸速度决定完成或取消过渡 if progress + panGR.velocity(in: nil).y / view.bounds.height > 0.3 { self.dismissView() Hero.shared.finish() } else { Hero.shared.cancel() } } }
解决方案
要实现侧滑返回时也用淡入淡出动画,需要从两个核心点修改:
1. 重写PageRoute的动画控制方法
默认PageRoute的buildPage仅负责页面内容渲染,路由切换的动画逻辑由buildTransitions控制。之前的代码只在buildPage里加了淡入动画,没覆盖pop场景的逻辑,修改如下:
class FadeRoute<T> extends PageRoute<T> { final Widget child; final RouteSettings routeSettings; FadeRoute({ required this.child, required this.routeSettings, }); @override RouteSettings get settings => routeSettings; @override Color? get barrierColor => Palette.black; @override String? get barrierLabel => null; @override Widget buildPage( BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, ) { return child; // 页面内容直接返回,动画交给buildTransitions处理 } @override Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { // animation在push时从0→1,pop时从1→0,直接关联淡入淡出 return FadeTransition( opacity: animation, child: child, ); } @override bool get maintainState => true; @override Duration get transitionDuration => const Duration(milliseconds: transitionDurationInMS); }
2. 自定义侧滑手势关联动画进度
iOS默认侧滑手势会触发系统滑动动画,需要替换为自定义手势,让淡入动画跟随拖拽进度变化,逻辑和你提供的iOS代码一致:
class TargetPage extends StatefulWidget { const TargetPage({super.key}); @override State<TargetPage> createState() => _TargetPageState(); } class _TargetPageState extends State<TargetPage> with SingleTickerProviderStateMixin { late final AnimationController _animController; double _dragProgress = 0.0; @override void initState() { super.initState(); _animController = AnimationController( vsync: this, duration: const Duration(milliseconds: transitionDurationInMS), value: 1.0, // 初始为完全显示状态 ); } @override void dispose() { _animController.dispose(); super.dispose(); } void _onDragUpdate(DragUpdateDetails details) { // 根据水平拖拽距离计算进度(基于屏幕宽度) _dragProgress = details.globalPosition.dx / MediaQuery.of(context).size.width; _animController.value = 1 - _dragProgress; } void _onDragEnd(DragEndDetails details) { if (_dragProgress > 0.3) { // 拖拽进度达标,完成返回 _animController.reverse().then((_) { Navigator.of(context).pop(); }); } else { // 进度不足,恢复页面显示 _animController.forward(); } } @override Widget build(BuildContext context) { return GestureDetector( onHorizontalDragUpdate: _onDragUpdate, onHorizontalDragEnd: _onDragEnd, child: FadeTransition( opacity: _animController, child: Scaffold( // 页面内容区域 appBar: AppBar(title: const Text("目标页面")), body: const Center(child: Text("侧滑返回时会淡入淡出")), ), ), ); } }
关键说明
buildTransitions中的animation参数会自动适配push/pop场景,无需额外判断;- 自定义手势时,通过拖拽进度实时更新动画控制器的value,实现和iOS代码一致的“跟随手势渐变”效果;
- 如果想保留系统侧滑手势的触发逻辑,也可以通过
CupertinoPageRoute的transitionBuilder参数替换动画,但自定义PageRoute+手势的方案可控性更强。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

