You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 14:13:13