Flutter point to effect switching of the interface

This article micro-channel public number "AndroidTraveler" episode.

background

We know that if the direct switch between pages, would be more blunt, but also allow users to feel very unexpected, the user experience is not very good.

Thus in general, the switching between the pages in order to achieve a smooth transition, will add animation.

In addition, sometimes we do not like the system's default animation, we want to be able to customize the animation.

Based on this, this chapter show on how to increase page switching Flutter Custom Animation.

Defaults

First we look at what the default effect?

It seems pretty good. code show as below:

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
      home: MyApp(),
    ));

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return _getCenterWidget(RaisedButton(
        child: Text('Go to next page->'),
        onPressed: () {
          Navigator.of(context).push(_createRoute());
        }));
  }
}

Route _createRoute() {
  return MaterialPageRoute(builder: (BuildContext context) => Page2());
}

class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return _getCenterWidget(Text('Page2'));
  }
}

Widget _getCenterWidget(Widget child) {
  return Scaffold(
    appBar: AppBar(),
    body: Center(
      child: child,
    ),
  );
}

We can see two pages created MyApp and Page2.

MyApp The first page has a button, there is a second page Page2 text.

The key switch in _createRoute () to create the route inside the method.

We point into MaterialPageRoute source code, you can see

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
    final PageTransitionsTheme theme = Theme.of(context).pageTransitionsTheme;
    return theme.buildTransitions<T>(this, context, animation, secondaryAnimation, child);
  }

Add comments beginning, know this is the default interface switching transition effects.

/// See also:
///
///  * [PageTransitionsTheme], which defines the default page transitions used
///    by [MaterialPageRoute.buildTransitions].

Also here you can see the default animation length of 300ms, and we can not be customized.

  @override
  Duration get transitionDuration => const Duration(milliseconds: 300);

Next we will talk about how to customize our interface switching transition effects, and long-time custom animations.

Custom Animation

1. Set PageRouteBuilder

From the above analysis we know that the most important place in the creation of routing method _createRoute () in.

First, change it so we do not use the default MaterialPageRoute , we use PageRouteBuilder .

Route _createRoute() {
  return PageRouteBuilder(
      pageBuilder: (context, animation, secondaryAnimation) => Page2(),
      transitionsBuilder:(context, animation, secondaryAnimation, child) {
        return child;
      }
  );
}

We can see by pageBuilder specified route page, by transitionsBuilder specified page transition effects.

Stated otherwise, the parameters here we do not rote, we point into the source code to see that, as follows:

/// Signature for the function that builds a route's primary contents.
/// Used in [PageRouteBuilder] and [showGeneralDialog].
///
/// See [ModalRoute.buildPage] for complete definition of the parameters.
typedef RoutePageBuilder = Widget Function(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation);

/// Signature for the function that builds a route's transitions.
/// Used in [PageRouteBuilder] and [showGeneralDialog].
///
/// See [ModalRoute.buildTransitions] for complete definition of the parameters.
typedef RouteTransitionsBuilder = Widget Function(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child);

If we run the code, due to the direct return Child , so it should be no animation effect. After we run, the effect is as follows:

2. 添加 Tween 和 SlideTransition

默认的过渡效果是从右边往左过来,我们这里自定义的演示效果就从下面往上过渡好了。

需要了解一下的是 Tween 是一个介于开始和结束值的线性插值器。

另外我们跟进上面的 transitionsBuilder 可以知道他的第一个 animation 参数取值为 0.0 到 1.0。

我们这边是从下往上,所以 y 轴的偏移就是由 1.0 到 0.0,表示竖直方向距离顶部一整个页面到不存在偏移(已经在顶部)。

因此关于 Tweenanimation 我们可以得到:

var begin = Offset(0.0, 1.0);
var end = Offset(0.0, 0.0);
var tween = Tween(begin: begin, end: end);
var offsetAnimation = animation.drive(tween);

因为我们是要实现滑动,因此将这个确定好的偏移动画通过 SlideTransition 处理并返回,可以得到修改后的路由代码如下:

Route _createRoute() {
  return PageRouteBuilder(
    transitionDuration: Duration(seconds: 5),
      pageBuilder: (context, animation, secondaryAnimation) => Page2(),
      transitionsBuilder:(context, animation, secondaryAnimation, child) {
        var begin = Offset(0.0, 1.0);
        var end = Offset(0.0, 0.0);
        var tween = Tween(begin: begin, end: end);
        var offsetAnimation = animation.drive(tween);

        return SlideTransition(
          position: offsetAnimation,
          child: child,
        );
      }
  );
}

效果如下:

看到上面效果,可能有小伙伴会有疑问。

问题一:你打开页面是从下到上我可以理解,但是返回为什么是反过来的从上到下呢?

我们跟进去 transitionsBuilder 的源码,可以看到

  /// Used to build the route's transitions.
  ///
  /// See [ModalRoute.buildTransitions] for complete definition of the parameters.
  final RouteTransitionsBuilder transitionsBuilder;

我们跟进去(通过点击)注释里面的 buildTransitions,可以看到 animation 的说明如下:

  ///  * [animation]: When the [Navigator] pushes a route on the top of its stack,
  ///    the new route's primary [animation] runs from 0.0 to 1.0. When the [Navigator]
  ///    pops the topmost route this animation runs from 1.0 to 0.0.

可以看到入栈和出栈的动画效果是相反的,而这个也符合我们的认知。

问题二:现在的效果是从下到上,如果我要实现从上到下,是不是将 begin 和 end 的 Offset 交换一下就可以?

其实如果你理解我上面说的这句话

我们这边是从下往上,所以 y 轴的偏移就是由 1.0 到 0.0,表示竖直方向距离顶部一整个页面到不存在偏移(已经在顶部)。

你就会知道,改成从 0.0 到 1.0 会是什么情况。

我们改一下,通过实际演示效果来说明。

修改后的代码如下:

Route _createRoute() {
  return PageRouteBuilder(
      pageBuilder: (context, animation, secondaryAnimation) => Page2(),
      transitionsBuilder:(context, animation, secondaryAnimation, child) {
        var begin = Offset(0.0, 0.0);
        var end = Offset(0.0, 1.0);
        var tween = Tween(begin: begin, end: end);
        var offsetAnimation = animation.drive(tween);

        return SlideTransition(
          position: offsetAnimation,
          child: child,
        );
      }
  );
}

仅仅是 begin 和 end 的 Offset 做了交换。

运行效果如下:

虽然能够看出一点端倪,但是我们前面讲过,默认动画时长是 300 ms,所以比较快,这样不好分析。

我们可以通过 PageRouteBuildertransitionDuration 属性来设置动画的时长。

我们设置 3s 来看下效果,代码如下:

Route _createRoute() {
  return PageRouteBuilder(
      transitionDuration: Duration(seconds: 3),
      pageBuilder: (context, animation, secondaryAnimation) => Page2(),
      transitionsBuilder:(context, animation, secondaryAnimation, child) {
        var begin = Offset(0.0, 0.0);
        var end = Offset(0.0, 1.0);
        var tween = Tween(begin: begin, end: end);
        var offsetAnimation = animation.drive(tween);

        return SlideTransition(
          position: offsetAnimation,
          child: child,
        );
      }
  );
}

运行效果如下:

看到了吧,确实是反过来了,从一开始距离顶部为 0.0,到距离顶部 1.0(100%)。

那么如果我想实现从上到下进入怎么办呢?

我们给一张图,相信看完你就懂了。

从这张图我们知道,如果我们从下往上,y 应该从 1.0 变到 0.0。如果要从上往下,y 应该从 -1.0 变到 0.0。

所以我们修改代码,如下:

Route _createRoute() {
  return PageRouteBuilder(
      transitionDuration: Duration(seconds: 3),
      pageBuilder: (context, animation, secondaryAnimation) => Page2(),
      transitionsBuilder:(context, animation, secondaryAnimation, child) {
        var begin = Offset(0.0, -1.0);
        var end = Offset(0.0, 0.0);
        var tween = Tween(begin: begin, end: end);
        var offsetAnimation = animation.drive(tween);

        return SlideTransition(
          position: offsetAnimation,
          child: child,
        );
      }
  );
}

运行效果为:

3. 通过 CurveTween 来点加速度

当我们将动画时长设置为 3s 之后,我们可以明显的看到我们的动画速度似乎是匀速的。

那么如果我想修改下动画的速度,比如进来快,结束慢,可不可以呢?

答案是肯定的。

我们通过 CurveTween 可以来实现这个需求。

使用的重点在于 curve 的选择,所以我们要选择哪种 curve 呢?

其实 Flutter 我比较喜欢的一个点就是代码注释详细,并且还有 demo 演示。

我们进入 Curves 源码,以 Curves.ease 为例:

  /// A cubic animation curve that speeds up quickly and ends slowly.
  ///
  /// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease.mp4}
  static const Cubic ease = Cubic(0.25, 0.1, 0.25, 1.0);

注释说了启动快,结束慢,而且还有一个 mp4 链接,点击可以看到如下效果:

我们可以看出它的变化趋势,通过斜率可以看出前期快,后期慢,而且右边还有四种动画的效果预览。

我们设置 CurveTween 代码如下:

var curveTween = CurveTween(curve: Curves.ease);

可以看到很简单,选择一种你想要的变化趋势即可。

4. 组合 Tween 和 CurveTween

这个也比较简单,通过 Tween 自带的 chain 方法即可,如下:

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

另外一般 Offset(0.0, 0.0) 可以直接写为 Offset.zero。

修改后代码为:

Route _createRoute() {
  return PageRouteBuilder(
      transitionDuration: Duration(seconds: 3),
      pageBuilder: (context, animation, secondaryAnimation) => Page2(),
      transitionsBuilder:(context, animation, secondaryAnimation, child) {
        var begin = Offset(0.0, 1.0);
        var end = Offset.zero;
        var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: Curves.ease));

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

运行效果如下:

5. 完整例子

有了上面的基础,我们就可以将四个方向的动画效果都加上,当然我们这边就不延时了。另外为了演示方便,就直接打开后 delay 1s 返回。

代码如下:

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
      home: MyApp(),
    ));

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return _getCenterWidget(Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        _getBtn(context, 'right in',
            Tween(begin: Offset(1.0, 0.0), end: Offset.zero)),
        _getBtn(context, 'left in',
            Tween(begin: Offset(-1.0, 0.0), end: Offset.zero)),
        _getBtn(context, 'bottom in',
            Tween(begin: Offset(0.0, 1.0), end: Offset.zero)),
        _getBtn(context, 'top in',
            Tween(begin: Offset(0.0, -1.0), end: Offset.zero)),
      ],
    ));
  }
}

Widget _getBtn(BuildContext context, String textContent, Tween<Offset> tween) {
  return RaisedButton(
      child: Text(textContent),
      onPressed: () {
        Navigator.of(context).push(_createRoute(tween));
      });
}

Route _createRoute(Tween<Offset> tween) {
  return PageRouteBuilder(
      pageBuilder: (context, animation, secondaryAnimation) => Page2(),
      transitionsBuilder: (context, animation, secondaryAnimation, child) {
        return SlideTransition(
          position:
              animation.drive(tween.chain(CurveTween(curve: Curves.ease))),
          child: child,
        );
      });
}

class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Future.delayed(Duration(seconds: 1), () {
      Navigator.of(context).pop();
    });
    return _getCenterWidget(Text('Page2'));
  }
}

Widget _getCenterWidget(Widget child) {
  return Scaffold(
    appBar: AppBar(),
    body: Center(
      child: child,
    ),
  );
}

效果如下:

结语

到了这里,基本就把 Flutter 界面之间的过渡说清楚了。

其他的比如旋转、缩放、透明度甚至组合动画,相信有了上面的基础,你也可以自行进行 DIY。

这里附上缩放的效果如下:

具体代码见 GitHub:
flutter_page_transition

参考链接:
Animate a page route transition
Tween class

更多阅读:
Flutter 即学即用系列博客
Flutter & Dart

Guess you like

Origin www.cnblogs.com/nesger/p/11489579.html