乐趣区

关于flutter:Flutter-实战动画序列共享动画路由动画

老孟导读:此篇文章是 Flutter 动画系列文章第四篇,本文介绍动画序列、共享动画、路由动画。

动画序列

Flutter 中组合动画应用 IntervalInterval 继承自Curve,用法如下:

Animation _sizeAnimation = Tween(begin: 100.0, end: 300.0).animate(CurvedAnimation(parent: _animationController, curve: Interval(0.5, 1.0)));

示意 _sizeAnimation 动画从 0.5(一半)开始到完结,如果动画时长为 6 秒,_sizeAnimation则从第 3 秒开始。

Intervalbeginend 参数值的范畴是 0.0 到 1.0。

上面实现一个先执行色彩变动,在执行大小变动,代码如下:

class AnimationDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _AnimationDemo();
}

class _AnimationDemo extends State<AnimationDemo>
    with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  Animation _colorAnimation;
  Animation _sizeAnimation;

  @override
  void initState() {
    _animationController =
        AnimationController(duration: Duration(seconds: 5), vsync: this)
    ..addListener((){setState(() {});});

    _colorAnimation = ColorTween(begin: Colors.red, end: Colors.blue).animate(
        CurvedAnimation(parent: _animationController, curve: Interval(0.0, 0.5)));

    _sizeAnimation = Tween(begin: 100.0, end: 300.0).animate(CurvedAnimation(parent: _animationController, curve: Interval(0.5, 1.0)));

    // 开始动画
    _animationController.forward();
    super.initState();}

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          Container(
              height: _sizeAnimation.value,
              width: _sizeAnimation.value,
              color: _colorAnimation.value),
        ],
      ),
    );
  }

  @override
  void dispose() {_animationController.dispose();
    super.dispose();}
}

成果如下:

咱们也能够设置同时动画,只需将 2 个 Interval 的值都改为Interval(0.0, 1.0)

设想上面的场景,一个红色的盒子,动画时长为 6 秒,前 40% 的工夫大小从 100->200,而后放弃 200 不变 20% 的工夫,最初 40% 的工夫大小从 200->300,这种成果通过 TweenSequence 实现,代码如下:

_animation = TweenSequence([
  TweenSequenceItem(tween: Tween(begin: 100.0, end: 200.0)
          .chain(CurveTween(curve: Curves.easeIn)),
      weight: 40),
  TweenSequenceItem(tween: ConstantTween<double>(200.0), weight: 20),
  TweenSequenceItem(tween: Tween(begin: 200.0, end: 300.0), weight: 40),
]).animate(_animationController);

weight示意每一个 Tween 的权重。

最终成果如下:

共享动画

Hero 是咱们罕用的过渡动画,当用户点击一张图片,切换到另一个页面时,这个页面也有此图,那么应用 Hero 组件就在适合不过了,先看下 Hero 的效果图:

下面成果实现的列表页面代码如下:

class HeroDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _HeroDemo();
}

class _HeroDemo extends State<HeroDemo> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GridView(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 5, mainAxisSpacing: 3),
        children: List.generate(10, (index) {if (index == 6) {
            return InkWell(onTap: () {
                Navigator.push(
                    context,
                    new MaterialPageRoute(builder: (context) => new _Hero1Demo()));
              },
              child: Hero(
                tag: 'hero',
                child: Container(
                  child: Image.asset(
                    'images/bird.png',
                    fit: BoxFit.fitWidth,
                  ),
                ),
              ),
            );
          }
          return Container(color: Colors.red,);
        }),
      ),
    );
  }
}

第二个页面代码如下:

class _Hero1Demo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(),
      body: Container(
          alignment: Alignment.topCenter,
          child: Hero(
            tag: 'hero',
            child: Container(
              child: Image.asset('images/bird.png',),
            ),
          )),
    );
  }
}

2 个页面都有 Hero 控件,且 tag 参数统一。

路由动画

转场 就是从以后页面跳转到另一个页面,跳转页面在 Flutter 中通过 Navigator,跳转到新页面如下:

Navigator.push(context, MaterialPageRoute(builder: (context) {return _TwoPage();
}));

回退到前一个页面:

Navigator.pop(context);

Flutter 提供了两个转场动画,别离为 MaterialPageRouteCupertinoPageRoute,MaterialPageRoute 依据不同的平台显示不同的成果,Android 成果为从下到上,iOS 成果为从左到右。CupertinoPageRoute 不分平台,都是从左到右。

应用 MaterialPageRoute 案例如下:

class NavigationAnimation extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(),
      body: Center(
        child: OutlineButton(child: Text('跳转'),
          onPressed: () {Navigator.push(context, CupertinoPageRoute(builder: (context) {return _TwoPage();
            }));
          },
        ),
      ),
    );
  }
}

class _TwoPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(),
      body: Container(color: Colors.blue,),
    );
  }
}

iOS 成果:

如果要自定义转场动画如何做?

自定义任何组件都是一样的,如果零碎有相似的,间接看源代码是如何实现的,而后依照它的模版自定义组件。

回到正题,看 MaterialPageRoute 的继承关系:

PageRoute 的继承关系:

MaterialPageRoute 和 CupertinoPageRoute 都是继承 PageRoute,所以重点是 PageRoute,PageRoute 是一个抽象类,其子类还有一个 PageRouteBuilder,看其名字就晓得这是一个能够自定义动画成果,PageRouteBuilder 源代码:

pageBuilder 示意跳转的页面。

transitionsBuilder 示意页面的动画成果,默认值代码:

Widget _defaultTransitionsBuilder(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {return child;}

通过源代码发现,默认状况下没有动画成果。

自定义转场动画只需批改 transitionsBuilder 即可:

Navigator.push(
    context,
    PageRouteBuilder(pageBuilder: (
      BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
    ) {return _TwoPage();
    }, transitionsBuilder: (BuildContext context,
        Animation<double> animation,
        Animation<double> secondaryAnimation,
        Widget child) {
      return SlideTransition(position: Tween(begin: Offset(-1, 0), end: Offset(0, 0))
            .animate(animation),
        child: child,
      );
    }));

将其封装,方便使用:

class LeftToRightPageRoute extends PageRouteBuilder {
  final Widget newPage;

  LeftToRightPageRoute(this.newPage)
      : super(
          pageBuilder: (
            BuildContext context,
            Animation<double> animation,
            Animation<double> secondaryAnimation,
          ) =>
              newPage,
          transitionsBuilder: (
            BuildContext context,
            Animation<double> animation,
            Animation<double> secondaryAnimation,
            Widget child,
          ) =>
              SlideTransition(position: Tween(begin: Offset(-1, 0), end: Offset(0, 0))
                .animate(animation),
            child: child,
          ),
        );
}

应用:

Navigator.push(context, LeftToRightPageRoute(_TwoPage()));

不仅是这些平移动画,后面所学的旋转、缩放等动画间接替换 SlideTransition 即可。

下面的动画只对新的页面进行了动画,如果想实现以后页面被新页面从顶部顶出的成果,实现形式如下:

class CustomPageRoute extends PageRouteBuilder {
  final Widget currentPage;
  final Widget newPage;

  CustomPageRoute(this.currentPage, this.newPage)
      : super(
          pageBuilder: (
            BuildContext context,
            Animation<double> animation,
            Animation<double> secondaryAnimation,
          ) =>
              currentPage,
          transitionsBuilder: (
            BuildContext context,
            Animation<double> animation,
            Animation<double> secondaryAnimation,
            Widget child,
          ) =>
              Stack(
            children: <Widget>[
              SlideTransition(
                position: new Tween<Offset>(begin: const Offset(0, 0),
                  end: const Offset(0, -1),
                ).animate(animation),
                child: currentPage,
              ),
              SlideTransition(
                position: new Tween<Offset>(begin: const Offset(0, 1),
                  end: Offset(0, 0),
                ).animate(animation),
                child: newPage,
              )
            ],
          ),
        );
}

实质就是对两个页面做动画解决,应用:

Navigator.push(context, CustomPageRoute(this, _TwoPage()));

除了自定义路由动画,在 Flutter 1.17 公布大会上,Flutter 团队还公布了新的 Animations 软件包,该软件包提供了实现新的 Material motion 标准的预构建动画。

外面提供了一系列动画,局部成果:

详情:https://juejin.im/post/6847902223909781511

交换

老孟 Flutter 博客地址(330 个控件用法):http://laomengit.com

欢送退出 Flutter 交换群(微信:laomengit)、关注公众号【老孟 Flutter】:

退出移动版