Flutter中实现类似Cupertino页面转场的前后路由联动动画方案咨询
我懂你想要的那种iOS风格页面转场——旧页面向左滑出一截,新页面从右侧滑入,而且两个页面的状态都得原封不动保留对吧?之前的方案踩坑了,因为你把旧页面的Widget重新传进新路由里渲染,等于重建了一个新实例,原来的状态肯定丢了。下面给你两个最优解决方案:
方案1:直接用官方CupertinoPageRoute(最简单)
如果你的需求就是标准的iOS左右滑转场,直接用Flutter自带的CupertinoPageRoute就行,它完美实现了你要的双页面动画,而且完全保留旧页面状态,不会重建任何Widget:
// 跳转时直接用这个路由 Navigator.of(context).push(CupertinoPageRoute(builder: (context) => Page2()));
哪怕你是在MaterialApp里用,这个路由也能正常工作——旧页面会自动向左滑出一部分,新页面从右侧滑入,还自带阴影过渡效果,完全符合你的需求。
方案2:自定义转场动画(个性化需求)
如果你需要调整滑出比例、动画时长或者阴影效果,别再手动传旧页面了!利用Flutter路由体系自带的secondaryAnimation(专门给前一个路由做动画的参数)来实现,这样不需要重建旧页面,状态100%保留:
class CustomSlideRoute<T> extends PageRouteBuilder<T> { final Widget child; final double oldPageSlideRatio; // 自定义旧页面滑出比例,比如0.3就是滑出30% CustomSlideRoute({ required this.child, this.oldPageSlideRatio = 0.3, }) : super( transitionDuration: const Duration(milliseconds: 700), reverseTransitionDuration: const Duration(milliseconds: 700), pageBuilder: (context, animation, secondaryAnimation) => child, ); @override Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { // 旧页面的动画:由secondaryAnimation驱动,向左滑出指定比例 final oldPageAnimation = Tween<Offset>( begin: Offset.zero, end: Offset(-oldPageSlideRatio, 0.0), ).animate(CurvedAnimation( parent: secondaryAnimation, curve: Curves.easeInOut, reverseCurve: Curves.easeInOut, )); // 新页面的动画:从右侧滑入,同时添加动态阴影 final newPageAnimation = Tween<Offset>( begin: const Offset(1.0, 0.0), end: Offset.zero, ).animate(CurvedAnimation( parent: animation, curve: Curves.easeInOut, reverseCurve: Curves.easeInOut, )); // 组合动画:不需要手动加旧页面,Flutter会自动把导航栈里的旧页面作为背景 return SlideTransition( position: oldPageAnimation, child: SlideTransition( position: newPageAnimation, child: AnimatedBuilder( animation: animation, builder: (context, child) { return Material( elevation: 10.0 * animation.value, // 阴影随动画进度增强 shadowColor: Colors.black.withOpacity(0.5 * animation.value), child: child, ); }, child: child, ), ), ); } } // 使用方式 Navigator.of(context).push(CustomSlideRoute(child: Page2()));
为什么这个方案能保留状态?
因为我们没有创建旧页面的新实例,而是直接操控导航栈里已经存在的旧页面——secondaryAnimation就是Flutter专门用来控制前一个路由动画的参数,旧页面的Widget实例完全没被重建,所以输入框内容、滚动位置这些状态都会原封不动保留。
之前的方案为什么会丢状态?
你之前传prevPage: widget,等于在新路由里重新渲染了一个旧页面的副本,这个副本和导航栈里原来的旧页面没有任何关联,原来的状态自然就丢失了。
内容的提问来源于stack exchange,提问作者bihire boris
相关产品推荐
相关产品推荐

