Flutter-animatio

120 阅读2分钟

Flutter-animation

  • 1.Animation:抽象类 无法实例化所以必须使用子类 *监听动画值的改变 *监听动画状态的改变 *value 获取当前值 *status 获取当前状态
  • 2.AnimationController继承自Animation *vsync同步信号,屏幕刷新率 一秒钟刷新60次(this-> with SingleTickerProviderStateMixin) *forward():向前执行动画 *reverse():反转执行动画
  • 3.CurvedAnimation *设置执行的速率,即速度曲线 4.Tween:设置动画执行的value范围 *begin开始值 *end 结束值

主要代码

// 1.创建AnimationController
  late AnimationController _controller;
  late CurvedAnimation animation;
  late Animation sizeAnim;
​
_controller = AnimationController(
  vsync: this,
  duration: Duration(milliseconds: 500),
  // lowerBound: 50,
  // upperBound: 150,
  animationBehavior: AnimationBehavior.preserve
​
);
// 设置curved的值
animation = CurvedAnimation(parent: _controller, curve: Curves.linear);
//设置值得范围
sizeAnim = Tween(begin: 50.0,end: 150.0).animate(animation);
 // 监听动画状态的改变
// 监听动画值的改变
_controller.addListener(() {
  setState(() {
  });
});
// 监听动画状态的改变
_controller.addStatusListener((status) {
  if(status ==  AnimationStatus.completed){
    _controller.reverse();
  }else if(status == AnimationStatus.dismissed){
    _controller.forward();
  }
});
///销毁动画控制器
void dispose() {
  _controller.dispose();
  super.dispose();
}
​

记录一下整体代码:

import 'package:draw_guess_flutter/utils.dart';
import 'package:flutter/material.dart';
​
class MYWordBankPage extends StatefulWidget {
  static const routerName = "/word_bank";
  @override
  State<MYWordBankPage> createState() => _MYWordBankPageState();
}
​
class _MYWordBankPageState extends State<MYWordBankPage> with SingleTickerProviderStateMixin {
  // 1.创建AnimationController
  late AnimationController _controller;
  late CurvedAnimation animation;
  late Animation sizeAnim;
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
​
    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 500),
      // lowerBound: 50,
      // upperBound: 150,
      animationBehavior: AnimationBehavior.preserve
​
    );
    // 设置curved的值
    animation = CurvedAnimation(parent: _controller, curve: Curves.linear);
​
    sizeAnim = Tween(begin: 50.0,end: 150.0).animate(animation);
    // 监听动画值的改变
    _controller.addListener(() {
      setState(() {
      });
    });
    // 监听动画状态的改变
​
    _controller.addStatusListener((status) {
      if(status ==  AnimationStatus.completed){
        _controller.reverse();
      }else if(status == AnimationStatus.dismissed){
        _controller.forward();
      }
    });
​
  }
  @override
  Widget build(BuildContext context) {
    // 只有收到同步信号的时候,才会进行帧绘制,在后台的时候不会绘制
    //  final controller = AnimationController(vsync: this);
    // final animation =  CurvedAnimation(parent: controller,curve: Curves.easeIn);
    // final valueAnimation = Tween(begin: 100,end: 200).animate(animation);
​
    return Scaffold(
      appBar: buildMJYNavBar("我的词库"),
      body: Center(
        child: Icon(Icons.favorite,color: Colors.red,size: sizeAnim.value,),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // _controller.forward();
          if (_controller.isAnimating){
            _controller.stop();
          }else if(_controller.status == AnimationStatus.completed || _controller.status == AnimationStatus.dismissed){
            _controller.forward();
          } else{
            if(_controller.status == AnimationStatus.forward){
              _controller.forward();
            }else{
              _controller.reverse();
            }
          }
        },
        child: Icon(Icons.play_arrow),
      ),
    );
  }
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}
​

以上代码缺点

  1. 每次写动画,都需要写一段代码
  2. setState -> build
优化方案一

将需要动画的部件用AnimatedWidget包裹,这样就不逼调用setState来使得不需要的部件也一直builder

class MJYAnimationIcon extends AnimatedWidget{
  const MJYAnimationIcon(Animation anim, {Key? key}) : super(key: key, listenable: anim);
  @override
  Widget build(BuildContext context) {
    Animation anim = listenable as Animation<double>;
    return Icon(Icons.favorite,color: Colors.red,size: anim.value,);
  }

缺点:

  1. 每次都需要创建一个类
  2. 如果构建的Widget有子类,那么子类依然会重复的builder
优化方案二

AnimationBuilder方案 类似Consumer

AnimatedBuilder(
  animation: _controller,
  builder: (ctx,child){
    return Icon(Icons.favorite,color: Colors.red,size: sizeAnim.value,);
  },
  child: Text("不想要Builder的Widget"),
)

交织动画

  • 需求
  • 大小变化
  • 颜色变化
  • 透明度变化
  • 旋转动画
 // 设置curved的值
    animation = CurvedAnimation(parent: _controller, curve: Curves.linear);
    _sizeAnim = Tween(begin: 50.0,end: 150.0).animate(animation);
    _colorAnim = ColorTween(begin: Colors.orange,end: Colors.purple).animate(animation);
    _opacityAnim = Tween(begin: 1.0,end: 0.5).animate(animation);
    _radiansAnim = Tween(begin: 0.0,end: 2 * pi).animate(animation);
AnimatedBuilder(
  animation: _controller,
  builder: (ctx,child){
    return Opacity(
      opacity: _opacityAnim.value,
      child: Transform(
        transform: Matrix4.rotationZ(_radiansAnim.value) ,
        alignment: Alignment.center,
        child: Container(
          width: _sizeAnim.value,
          height: _sizeAnim.value,
          // transform: Matrix4.rotationZ(pi/4),
          color: _colorAnim.value,
        ),
      ),
    );
  },
  // child:
)

也就是多了一些tween

转场动画

 //普通从右往左转场动画
Navigator.of(context).push(MaterialPageRoute(
     builder: (ctx){
   return MyPaintingsPage();
 },
 fullscreenDialog: true,
 )
 );
//modal动画
Navigator.of(context).push(
  PageRouteBuilder(
      transitionDuration: Duration(seconds: 3),
      pageBuilder: (ctx, anim1, anim2) {
        return FadeTransition(
          opacity: anim1,
          child: MyPaintingsPage(),
        );
      }),
);
7yhd7-1gqv0.gif

Hero动画

Hero(
  tag: myPaintings[index]["img"],
  child: Container()
)
  Hero(
  tag: _imgUrl,
  child: Image.network(_imgUrl))),     
),

两个Hero中的tag值要相等

效果

p7s22-rnbgj.gif