Flutter 基础动画 Hero动画 交错动画

Flutter动画学习
效果直接贴代码 运行看吧,更改push入口查看不同动画效果。
基础动画:颜色渐变、控件大小更改、位置变更
Hero动画:从一个界面push到另一个界面,有一个控件跟着动来动去。
交错动画:把基础动画结合起来,curve 0.0-1.0来控制好几个基础动画执行的先后顺序。

基础动画

动画过程可以是匀速的、加速的或者先加速后减速等。Flutter中通过Curve(曲线)来描述动画过程,Curve可以是线性的(Curves.linear),也可以是非线性的。
常用的曲线(Curve)类型
每种UI框架基本都有这些动画的基础类型
Flutter中,继承Curve类,可以自己定义不同的曲线类型。

继承关系
CurvedAnimation->Animation->Listenable
AnimationController->Animation->Listenable

  • AnimationController
    初始化AnimationController
    final AnimationController animation = new AnimationController(vsync: null)

  • Ticker
    TickerProvider创建Ticker

  • Tween
    默认情况下,AnimationController对象值的范围是0.0到1.0
    Tween.animate()
    要使用Tween对象就要调用animate()方法

  • AnimatedWidget
    animatedWidget替代下面代码
    将要执行的Widget集成AnimatiedWidget

..addListener(() {
    setState(() {
  });
});
  • AnimatedBuilder
    正是将渲染逻辑分离出来
  • addStatusListener()
    动画状态监听
    <colgroup><col style=”width: 130px;”><col style=”width: 406px;”></colgroup>
dismissed动画在起始点停止
forward动画正在正向执行
reverse动画正在反向执行
completed动画在终点停止

图片资源配置

flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
    - images/IMG_0696.jpg
import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Animation Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new AnimatioStateRoute(),
    );
  }
}

class AnimatioStateRoute extends StatefulWidget {
  @override
  _AnimatioStateRouteState createState() => _AnimatioStateRouteState();
}

class _AnimatioStateRouteState extends State<AnimatioStateRoute> with SingleTickerProviderStateMixin {

  Animation<double> animation; // 保存动画的插值和状态
  AnimationController controller; // 控制动画 开始、暂停等

  @override
  void initState() {
    super.initState();
    // 初始化控制动画类 动画用时4秒
    controller = new AnimationController(duration: const Duration(seconds: 4), vsync: this);
    // 动画曲线类型
    animation = CurvedAnimation(parent: controller, curve: Curves.bounceInOut);
    // 动画形态变化(大小从0.0->200.0)监听
    animation = new Tween(begin: 0.0, end: 200.0).animate(controller);
    animation.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        //动画执行结束时反向执行动画
        controller.reverse();
        print("动画完成");
      } else if (status == AnimationStatus.dismissed) {
        //动画恢复到初始状态时执行动画(正向)
        controller.forward();
        print("初始化状态");
      } else if (status == AnimationStatus.forward) {
        print("动画开始");
      } else if (status == AnimationStatus.reverse) {
        print("动画重复");
      }
    });
//    controller.forward();
  }

  void forward() {
    // 开始执行动画
    controller.forward();
  }

  void stop() {
    controller.stop();
  }

  void reverse() {
    controller.reverse();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Padding(padding: new EdgeInsets.only(top: 50.0), child: new Column(
        children: <Widget>[
          new Center(
            child: _GrowTransition(
              child: new Image.asset("images/IMG_0696.jpg"),
              animation: this.animation,
            ),
          ),
          new Container(
            child: new FlatButton(onPressed: () {
              forward();
            }, child: new Text("开始")),
          ),
          new Container(
            child: new FlatButton(onPressed: () {
              stop();
            }, child: new Text("暂停")),
          ),
          new Container(
            child: new FlatButton(onPressed: () {
              reverse();
            }, child: new Text("重复")),
          ),
          new Container(
            child: new FlatButton(onPressed: () {
              Navigator.push(context, FadeRoute(
                  builder: (context){
//                    return HeroAnimationRoute(); // Hero动画
//                    return StaggerDemo(); // 交错动画
                    return ItemPage(mTitle: "push",);// FadeRoute push动画
                  }, isActive: true
              )).then((value) {
                print(value);
              });
            }, child: new Text("跳转动画")),
          )
        ],
      ),
      ),
    );

  }

  dispose() {
    // 销毁
    controller.dispose();
    super.dispose();
  }
}

//
class _GrowTransition extends StatelessWidget {
  _GrowTransition({this.child, this.animation});

  final Widget child;
  final Animation<double> animation;

  Widget build(BuildContext context) {
    return new Center(
      child: new AnimatedBuilder(
          animation: animation,
          builder: (BuildContext context, Widget child) {
            return new Container(
                height: animation.value,
                width: animation.value,
                child: child
            );
          },
          child: child
      ),
    );
  }
}



class ItemPage extends StatelessWidget {
  ItemPage({Key key, this.mTitle}) : super(key : key);
  final String mTitle;
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Row(
        children: <Widget>[
          new Center(
            child: new Text("ItemPage"),
          ),
          new FlatButton(onPressed: () {
            Navigator.of(context).pop('返回值');
          }, child: new Text(mTitle + "-返回"))
        ],
      )
    );
  }
}

// 继承PageRoute类可以实现各种转场动画
class FadeRoute extends PageRoute {
  FadeRoute({
    @required this.builder,
    this.transitionDuration = const Duration(milliseconds: 300),
    this.opaque = true,
    this.barrierDismissible = false,
    this.barrierColor,
    this.barrierLabel,
    this.maintainState = true,
    this.isActive = true,
  });

  final WidgetBuilder builder;

  @override
  final Duration transitionDuration;

  @override
  final bool opaque;

  @override
  final bool barrierDismissible;

  @override
  final Color barrierColor;

  @override
  final String barrierLabel;

  @override
  final bool maintainState;
  @override
  final bool isActive;

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation) => builder(context);

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    // 001 平移
    return SlideTransition(position: new Tween<Offset>(
      begin: const Offset(1.0, 0.0),
      end: const Offset(0.0, 0.0),
    ).animate(animation), child: builder(context),);
    // 002 渐变
    //当前路由被激活,是打开新路由
    if(isActive) {
      return FadeTransition(
        opacity: animation,
        child: builder(context),
      );
    }else{
      //是返回,则不应用过渡动画
      return Padding(padding: EdgeInsets.zero);
    }
  }
}


class HeroAnimationRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Container(
        alignment: Alignment.topCenter,
        child: InkWell(
          child: Hero(
            tag: "tag same", //唯一标记,前后两个路由页Hero的tag必须相同
            child: ClipOval(
              child: Image.asset("images/IMG_0696.jpg",
                width: 50.0,
              ),
            ),
          ),
          onTap: () {
            //打开B路由
            Navigator.push(context, PageRouteBuilder(
                pageBuilder: (BuildContext context, Animation animation,
                    Animation secondaryAnimation) {
                  return new FadeTransition(
                      opacity: animation,
                      child: HeroAnimationRouteB(imageName: "images/IMG_0696.jpg"),
                  );
                })
            );
          },
        ),
      ),
    );
  }
}

class HeroAnimationRouteB extends StatelessWidget {
  HeroAnimationRouteB({this.imageName});
  final String imageName;

  @override Widget build(BuildContext context) {
    return new Scaffold(
      //唯一标记,前后两个路由页Hero的tag必须相同
      body: new Center( child: Hero( tag: "tag same",
          child: Image.asset(imageName,
            width: 350.0,
          )
        ),
      ),
    );
  }
}

// 交错动画
class StaggerAnimation extends StatelessWidget {
  StaggerAnimation({ Key key, this.controller }): super(key: key){
    // 动画的顺序使用curve控制 0.0-1.0
    //高度动画
    height = Tween<double>(
      begin:.0 ,
      end: 300.0,
    ).animate(
      CurvedAnimation(
        parent: controller,
        curve: Interval(
          0.0, 0.3, //间隔,前60%的动画时间
          curve: Curves.ease,
        ),
      ),
    );
    // 颜色动画
    color = ColorTween(
      begin:Colors.green ,
      end:Colors.red,
    ).animate(
      CurvedAnimation(
        parent: controller,
        curve: Interval(
          0.3, 0.6,//间隔,前60%的动画时间
          curve: Curves.ease,
        ),
      ),
    );
    // 偏移动画
    padding = Tween<EdgeInsets>(
      begin:EdgeInsets.only(left: .0),
      end:EdgeInsets.only(left: 100.0),
    ).animate(
      CurvedAnimation(
        parent: controller,
        curve: Interval(
          0.6, 1.0, //间隔,后40%的动画时间
          curve: Curves.ease,
        ),
      ),
    );
  }

  final Animation<double> controller;
  Animation<double> height;
  Animation<EdgeInsets> padding;
  Animation<Color> color;

  Widget _buildAnimation(BuildContext context, Widget child) {
    return new Scaffold(
      body: new Container(
        alignment: Alignment.bottomCenter,
        padding:padding.value ,
        child: Container(
          color: color.value,
          width: 50.0,
          height: height.value,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      builder: _buildAnimation,
      animation: controller,
    );
  }
}

class StaggerDemo extends StatefulWidget {
  @override
  _StaggerDemoState createState() => _StaggerDemoState();
}

class _StaggerDemoState extends State<StaggerDemo> with TickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
        duration: const Duration(milliseconds: 2000),
        vsync: this
    );
  }


  void _playAnimation() async {
    try {
      //先正向执行动画
      await _controller.forward().orCancel;
      //再反向执行动画
      await _controller.reverse().orCancel;
    } on TickerCanceled {
      // the animation got canceled, probably because we were disposed
    }
  }

  @override
  Widget build(BuildContext context) {
    return  GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: () {
        _playAnimation();
      },
      child: Center(
        child: Container(
          width: 300.0,
          height: 300.0,
          decoration: BoxDecoration(
            color: Colors.black.withOpacity(0.1),
            border: Border.all(
              color:  Colors.black.withOpacity(0.5),
            ),
          ),
          //调用我们定义的交错动画Widget
          child: StaggerAnimation(
              controller: _controller
          ),
        ),
      ),
    );
  }
}
    原文作者:GA_
    原文地址: https://www.jianshu.com/p/932a9201d38f
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞