Flutter 路由动画Offset小记

5,022 阅读3分钟

刚刚看了一下flutter的路由动画文档,地址是:page-route-animation

PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => Page2(),
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      var begin = Offset(0.0, -1.0);
      var end = Offset.zero;
      var curve = Curves.ease;

      var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));

      return SlideTransition(
        position: animation.drive(tween),
        child: child,
      );
    },
  )

针对动画,学习了两点:

  1. Flutter offset的值是怎么回事,1 0 -1分别代表什么,我整理了一下,如图:

我来解读一下,

1. 页面显示在屏幕时,Offset的dx dy均为0;
2. 如果需要动画页面从屏幕底部弹出,则应该是dy=1 到 dy=0;
3. 如果需要动画页面从右侧推入到屏幕,则应该是dx=1 到 dx=0;
4. 如果需要动画页面从屏幕顶部弹出,则应该是dy=0 到 dy=-1
5. 其他类似

建议大家使用文档里的案例操作实验一下,加深印象。学习了这个,在写动画时就不会出现乱套的情况了。

  1. secondaryAnimation是干嘛用的呢?

写过安卓的都清楚有进场和出场动画,overridexxxx,那flutter只通过上面这段代码怎么实现出场,就给了一个child,很是奇怪。然后我发现secondaryAnimation还没用,是干嘛的?翻阅的一些文档: buildTransitions

看到了使用方式:

transitionsBuilder: (
      BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
      Widget child,
  ) {
    return SlideTransition(
      position: AlignmentTween(
        begin: const Offset(0.0, 1.0),
        end: Offset.zero,
      ).animate(animation),
      child: SlideTransition(
        position: TweenOffset(
          begin: Offset.zero,
          end: const Offset(0.0, 1.0),
        ).animate(secondaryAnimation),
        child: child,
      ),
    );
  }

刚看到我是很惊讶的,这是个怎么操作法?进出都套在了一个child上,到底怎么运行的呢?一开始我猜想难道是源码使用的地方会取出child的child?看了源码后,我觉得设计真巧妙!!

首先介绍下这两个动画常量(animation.dart中,目的就是保持动画终止位置和保持动画起始位置):

kAlwaysCompleteAnimation // The animation is stopped at the end

kAlwaysDismissedAnimation // The animation is stopped at the beginning

看到这里大家可能会有点明白了,两个animation是不是用上面两个常量,来切换使用做到每次只运行一个的呢?

然后我用下面的动画(一段动画,新页面从右侧屏幕滑入,旧页面从屏幕左侧滑出),打印了一下对应的回调:

SlideTransition(
      position: Tween<Offset>(
        begin: const Offset(1.0, 0.0),
        end: Offset.zero,
      ).animate(animation),
      child: SlideTransition(
        position: Tween<Offset>(
          begin: Offset.zero,
          end: const Offset(-1.0, 0.0),
        ).animate(secondAnimation),
        child: child,
      ),
    )

得到的log,简化后:

animationsencondaryAnimation
AnimationController#1c77c(▶ 0.335kAlwaysDismissedAnimation
AnimationController#fc1fe(⏭ 1.000AAnimationController#1c77c(▶ 0.335
AnimationController#1c77c(▶ 0.535kAlwaysDismissedAnimation
AnimationController#fc1fe(⏭ 1.000AAnimationController#1c77c(▶ 0.535
AnimationController#1c77c(⏭ 1.000kAlwaysDismissedAnimation
AnimationController#fc1fe(⏭ 1.000;AnimationController#1c77c⏭ 1.000

先只看#1c77c这个animation:

发现第一个参数是从0-1做的动画,第二个参数是保持不变在start状态即Offset.zero,即通过改变第一个widge的位置,来实现界面从右侧滑入,第二个widget一直保持可见状态。

再来看下fc1fe这个animation,发现这个动画是第一个widge保持在end状态即Offset.zero(屏幕上),第二个动画是从0-1运行,即offset.dx从0 -> -1,也就是页面从屏幕往左侧滑出的动画。

由以上,我们可以得出结论,animation参数值是用来给新push的页面做进场动画的,secondaryAnimation是给前页面做出厂动画的,灵活运用两个动画的变化值来实现动画,参数只需要一个child即可,我也从中学习了一种新的思路,建议大家也测试感受下。