前语

flutter 开发中难免会碰到一些动画操作,且运用率最高的动画基本上都是根底动画,本篇文章首要讲解一些常用的根底动画的运用

ps:假如下面的根底动画不太满足要求,那么能够选用自定义的方法,其他地方应该有,动画,即动起来的画面,必要的时候,咱们能够合作定时器(requestAnimationFrame)更新视图,也能完成咱们的动画作用,便是作用或许不是那么抱负,一般情况下根底动画也够用了

事例demo

动画

常用的根底动画首要分为隐式动画显式动画

ps:运用时需求留意特点更新时会对布局的影响,合理挑选布局,防止因为参加动画造成不相同的作用

隐式动画

咱们更改某些组件特点的一起,会自动触发组件躲藏的特点动画,因而此类动画被称为隐式动画,其在其他平台也应该常听到,运用很便利,但无法阻挠动画的暂停和持续

常见的隐式动画控件有:AnimatedPositionedAnimatedContainerAnimatedRotationAnimatedScaleAnimatedOpacity

更新缩放份额 AnimatedScale

double scale = 1;
AnimatedScale(
  duration: const Duration(seconds: 2),
  scale: scale,
  child: Container(
    color: Colors.green,
    width: 80,
    height: 80,
    child: TextButton(
      onPressed: () {
        if (scale > 1) {
          setState(() {
            scale = 1;
          });
        }else {
          setState(() {
            scale = 2;
          });
        }
      },
      child: const Text(
        '缩放动画',
        style: TextStyle(color: Colors.white),
      ),
    ),
  ),
),

更新透明度 AnimatedOpacity

double opacity = 1;
AnimatedOpacity(
  duration: const Duration(seconds: 2),
  opacity: opacity,
  child: Container(
    color: Colors.green,
    width: 100,
    height: 100,
    child: TextButton(
      onPressed: () {
        if (opacity < 1) {
          setState(() {
            opacity = 1;
          });
        }else {
          setState(() {
            opacity = 0.1;
          });
        }
      },
      child: const Text(
        '缩放动画',
        style: TextStyle(color: Colors.white),
      ),
    ),
  ),
),

旋转 AnimatedRotation

旋转参数比较特殊,与其说特殊不如说失常,往常的都是填写视点、弧度等,这儿的是填写百分比,且为占用一圈的百分比,例如:1 则为顺时针 360,半圈便是 0.5

double turns = 0; //旋转视点占一圈的百分比
AnimatedRotation(
  //旋转占一圈的百分比,360便是1,半圈便是 0.5,很奇怪是吧,哈哈,公式是这样的
  //即:旋转 90,便是 1/4 圈,即 0.25圈
  turns: turns, //旋转占一圈的百分比
  duration: const Duration(seconds: 2),
  child: Container(
    color: Colors.green,
    width: 100,
    height: 100,
    child: TextButton(
      onPressed: () {
        if (turns > 0) {
          setState(() {
            turns = 0;
          });
        } else {
          setState(() {
            //旋转 90,便是 1/4 圈,即 0.25圈
            turns =  0.25;
          });
        }
      },
      child: const Text(
        '旋转动画',
        style: TextStyle(color: Colors.white),
      ),
    ),
  ),
),

更新方位等组合动画

通过上面也能够看到,隐式动画组件基本上和一般组件相同,只不过前面加上了 Animated,因而咱们能够像更新一般组件相同运用它们,一起他们也支撑一起更新多个特点,即构成组合动画

double top = 0;
double left = 0;
double width = 80;
double height = 80;
Color color = Colors.green;
//假如打开Animated最初会发现很多类似组件,例如:Padding等,这儿面Container也改变了色彩等特点,也都支撑
//依据名字对应根底组件就知道动画是干什么的,很简略,就不多介绍了
AnimatedPositioned(
  duration: const Duration(seconds: 2),
  curve: Curves.easeInOut,
  left: left,
  top: top,
  child: AnimatedContainer(
    duration: const Duration(seconds: 2),
    curve: Curves.easeInOut,
    color: color,
    width: width,
    height: height,
    child: TextButton(
      onPressed: () {
        if (left > 0) {
          setState(() {
            color = Colors.green;
            left = 0;
            top = 0;
            width = 80;
            height = 80;
          });
        } else {
          setState(() {
            color = Colors.yellow;
            left = 160;
            top = 400;
            width = 160;
            height = 160;
          });
        }
      },
      child: const Text(
        '位移大小动画',
        style: TextStyle(color: Colors.white),
      ),
    ),
  ),
),

显式动画

显式动画,是依据 Animation 相关动画功用模块构成的动画,支撑暂停和持续,能够更好的操控动画的周期变化

AnimationController:动画操控器,能够操控方向,暂停,指定到某个值等,详细的能够点击去查看,需求继承 SingleTickerProviderStateMixin

Animation<T>:动画事情,用于更新动画某个特点

更新缩放

class _ScaleTransitionWidgetState extends State<ScaleTransitionWidget> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;
  @override
  void initState() {
    super.initState();
    //操控动画
    _controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
    //设置补间
    _animation = Tween<double>(
      begin: 1,
      end:  4,
    ).animate(_controller);
    //能够设置补间动画动画的动画特性或许运动曲线
    // .animate(
    //   CurvedAnimation(parent: _controller, curve: Curves.easeIn),
    // );
    //正向履行动画
    _controller.forward();
    //反向履行动画
    _controller.reverse();
    //停止动画
    _controller.stop();
    //动画是否履行完毕
    _controller.isCompleted;
  }
  //直接更新缩放的显式动画组件
  ScaleTransition(
      scale: _animation,
      child: Container(
        color: Colors.green,
        width: 80,
        height: 80,
        child: TextButton(
          onPressed: () {
            if (_controller.isCompleted) {
              _controller.reverse();
            }else {
              _controller.forward();
            }
          },
          child: const Text(
            '缩放动画',
            style: TextStyle(color: Colors.white),
          ),
        ),
      ),
  ),

更新透明度

late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
    super.initState();
    //操控动画
    _controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
    //设置补间
    _animation = Tween<double>(
      begin: 1,
      end:  0.1,
    ).animate(_controller);
}
FadeTransition(
  opacity: _animation,
  child: Container(
    color: Colors.green,
    width: 120,
    height: 120,
    child: TextButton(
      onPressed: () {
        if (_controller.isCompleted) {
          _controller.reverse();
        }else {
          _controller.forward();
        }
      },
      child: const Text(
        'fade动画',
        style: TextStyle(color: Colors.white),
      ),
    ),
  ),
),

旋转

late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
    super.initState();
    //操控动画
    _controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
    //设置补间
    _animation = Tween<double>(
      begin: 0,
      end:  0.25,
    ).animate(_controller);
}
RotationTransition(
  turns: _animation,
  child: Container(
    color: Colors.green,
    width: 120,
    height: 120,
    child: TextButton(
      onPressed: () {
        if (_controller.isCompleted) {
          _controller.reverse();
        }else {
          _controller.forward();
        }
      },
      child: const Text(
        '旋转动画',
        style: TextStyle(color: Colors.white),
      ),
    ),
  ),
),

组合动画(显式动画中推荐)

看了上面的 Transition 操作,感觉也不是那么好用,功用单一,代码还多了,且有些改换操作还贼难用,下面有一个显式动画中比较常用的组合控件,能够防止运用上面的一些列 Transition,且愈加通用

AnimatedBuilder:组合动画中比较常用的组件,能够防止运用 Transition 系列,其合作根底动画类,运用一般的组件 UI 即可完成动画作用,且不仅仅是组合动画,单个动画也能够哈

如下所示,咱们定义多个 Animation 事情,然后应用到组件中即可构成组合动画了(单个便是用一个 Animation 即可)

class _CombinAnimatedWidgetState extends State<CombinAnimatedWidget>  with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _widthAnimation;
  late Animation<double> _opacityAnimation;
  late Animation<BorderRadius> _borderRadiusAnimation;
  late Animation<double> _borderWidthAnimation;
  @override
  void initState() {
    super.initState();
    //操控动画
    _controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
    //设置补间
    _widthAnimation = Tween<double>(
      begin: 100,
      end:  240,
    ).animate(_controller);
    _opacityAnimation = Tween<double>(
      begin: 1,
      end:  0.6,
    ).animate(_controller);
    _borderRadiusAnimation = Tween<BorderRadius>(
      begin: const BorderRadius.all(Radius.circular(0)),
      end:  const BorderRadius.all(Radius.circular(120)),
    ).animate(_controller);
    _borderWidthAnimation = Tween<double>(
      begin: 1,
      end:  20,
    ).animate(_controller);
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("组合动画"),
      ),
      body: Center(
        child: GestureDetector(
          onTap: () {
            if (_controller.isCompleted) {
              _controller.reverse();
            }else {
              _controller.forward();
            }
          },
          //咱们的动画组件,内部能够运用咱们常用的控件
          child: AnimatedBuilder(
            animation: _controller,
            builder: (BuildContext context, Widget? child) {
              return Opacity(
                opacity: _opacityAnimation.value,
                child: Container(
                  width: _widthAnimation.value,
                  height: _widthAnimation.value,
                  decoration: BoxDecoration(
                    color: Colors.blue,
                    borderRadius: _borderRadiusAnimation.value,
                    border: Border.all(
                        width: _borderWidthAnimation.value,
                        color: Colors.cyanAccent
                    ),
                  ),
                ),
              );
            },
          ),
        ),
      ),
    );
  }
}

最后

根底动画,便是这么简略,运用时需求留意别被外部布局挤压等影响,以造成动画作用不正常的问题,一般运用 Stack 布局作用更好