likes
comments
collection
share

flutter-常用基础动画

作者站长头像
站长
· 阅读数 7

前言

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 布局效果更好